├── dist └── .keep ├── public ├── .gitkeep └── inject-cordova.js ├── tests ├── unit │ └── .gitkeep ├── dummy │ ├── public │ │ ├── .gitkeep │ │ ├── robots.txt │ │ └── crossdomain.xml │ ├── app │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ ├── routes │ │ │ ├── .gitkeep │ │ │ └── nav-bar │ │ │ │ ├── index.js │ │ │ │ ├── page-1.js │ │ │ │ └── options-from-model.js │ │ ├── styles │ │ │ ├── .gitkeep │ │ │ └── app.css │ │ ├── views │ │ │ └── .gitkeep │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── nav-bar.js │ │ ├── templates │ │ │ ├── .gitkeep │ │ │ ├── components │ │ │ │ └── .gitkeep │ │ │ ├── application.hbs │ │ │ └── nav-bar.hbs │ │ ├── resolver.js │ │ ├── router.js │ │ ├── app.js │ │ └── index.html │ ├── .jshintrc │ └── config │ │ └── environment.js ├── helpers │ ├── destroy-app.js │ ├── resolver.js │ ├── start-app.js │ ├── module-for-acceptance.js │ └── describe-app.js ├── test-helper.js ├── index.html ├── .jshintrc └── integration │ └── nav-bar-test.js ├── .npmignore ├── .bowerrc ├── app ├── templates │ ├── components │ │ └── cdv-nav-bar.hbs │ └── cdv-generic-nav-bar.hbs ├── components │ └── cdv-nav-bar.js ├── services │ └── cordova.js └── initializers │ └── in-app-livereload.js ├── node-tests ├── helpers │ ├── noop.js │ ├── mocks.js │ ├── stub.js │ └── _helper.js ├── fixtures │ └── project │ │ ├── .ember-cdv │ │ └── cordova │ │ └── config.xml └── unit │ ├── models │ └── project-with-config-test.js │ ├── tasks │ ├── add-platforms-test.js │ ├── verify-dist-test.js │ ├── update-config-xml-version-test.js │ ├── cordova-test.js │ ├── create-cordova-project-test.js │ ├── update-config-xml-test.js │ ├── link-environment-test.js │ ├── open-test.js │ ├── build-test.js │ ├── post-build-test.js │ └── archive-test.js │ └── addon-test.js ├── .watchmanconfig ├── .hound.yml ├── blueprints ├── cordova-starter-kit │ ├── files │ │ ├── app │ │ │ ├── transitions.js │ │ │ ├── templates │ │ │ │ └── application.hbs │ │ │ ├── adapters │ │ │ │ └── application.js │ │ │ ├── routes │ │ │ │ └── application.js │ │ │ └── styles │ │ │ │ └── app.scss │ │ └── config │ │ │ └── environment.js │ └── index.js └── cordova-init │ ├── files │ ├── gitignore │ └── config │ │ └── environment.js │ └── index.js ├── testem.json ├── lib ├── utils │ ├── default-platform.js │ ├── string.js │ ├── open.js │ └── run-command.js ├── commands │ ├── index.js │ ├── open.js │ ├── cordova.js │ ├── build.js │ ├── prepare.js │ └── archive.js ├── ui │ └── index.js ├── tasks │ ├── add-platforms.js │ ├── create-cordova-project.js │ ├── update-config-xml-version.js │ ├── cordova.js │ ├── verify-dist.js │ ├── open.js │ ├── link-environment.js │ ├── build.js │ ├── modify-xml.js │ ├── update-config-xml.js │ ├── update-config-xml-android-version-code.js │ ├── post-build.js │ └── archive.js ├── models │ └── project-with-config.js └── ext │ └── promise.js ├── .travis.yml ├── bower.json ├── addon ├── utils │ └── redirect.js ├── initializers │ └── in-app-livereload.js ├── mixins │ ├── controllers │ │ └── nav-bar.js │ ├── cordova-events.js │ └── routes │ │ └── nav-bar.js └── services │ └── cordova.js ├── .ember-cli ├── .gitignore ├── ember-cli-build.js ├── .editorconfig ├── .jshintrc ├── docs ├── faq.md ├── configuration.md ├── events.md ├── commands.md ├── nav-bar.md └── getting-started.md ├── LICENSE ├── package.json ├── index.js └── README.md /dist/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test-app 2 | tests 3 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /app/templates/components/cdv-nav-bar.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} 2 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /node-tests/helpers/noop.js: -------------------------------------------------------------------------------- 1 | module.exports = function() {}; 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | javascript: 2 | enabled: true 3 | config_file: .jshintrc 4 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/app/transitions.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /app/components/cdv-nav-bar.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Component.extend({ 4 | tagName: 'header' 5 | }); 6 | -------------------------------------------------------------------------------- /node-tests/fixtures/project/.ember-cdv: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TestApp", 3 | "modulePrefix": "test-app", 4 | "id": "com.poetic.test-app" 5 | } 6 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/nav-bar.hbs: -------------------------------------------------------------------------------- 1 | {{#cdv-nav-bar}} 2 | {{partial 'cdv-generic-nav-bar'}} 3 | {{/cdv-nav-bar }} 4 | 5 | {{outlet}} 6 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /app/services/cordova.js: -------------------------------------------------------------------------------- 1 | /* jshint esnext:true */ 2 | 3 | import CordovaService from 'ember-cli-cordova/services/cordova'; 4 | 5 | export default CordovaService.extend({}); 6 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "qunit", 3 | "test_page": "tests/index.html", 4 | "launch_in_ci": ["PhantomJS"], 5 | "launch_in_dev": ["PhantomJS", "Chrome"] 6 | } 7 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to ember-cli-cordova!

2 | 3 | {{liquid-outlet name="main"}} 4 | 5 | {{liquid-outlet modal}} 6 | -------------------------------------------------------------------------------- /lib/utils/default-platform.js: -------------------------------------------------------------------------------- 1 | module.exports = function defaultPlatform(project) { 2 | var config = project.config().cordova || {}; 3 | return config.platform || 'ios'; 4 | }; 5 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/nav-bar.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import NavBarMixin from 'ember-cli-cordova/mixins/controllers/nav-bar'; 3 | 4 | export default Ember.Controller.extend(NavBarMixin); 5 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/app/adapters/application.js: -------------------------------------------------------------------------------- 1 | import DS from 'ember-data'; 2 | import config from '../config/environment'; 3 | 4 | export default DS.ActiveModelAdapter.extend({ 5 | host: config.apiUrl 6 | }); 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | install: 10 | - npm install -g bower 11 | - npm install 12 | - bower install 13 | 14 | script: 15 | - npm test 16 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-cordova", 3 | "dependencies": { 4 | "ember": "~2.3.1", 5 | "ember-cli-shims": "0.1.0", 6 | "ember-cli-test-loader": "0.2.2", 7 | "ember-qunit-notifications": "0.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /addon/utils/redirect.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function(url) { 4 | if(window.location.href.indexOf('file://') > -1) { 5 | Ember.run.later(function() { 6 | window.location.replace(url); 7 | }, 50); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/commands/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'cordova': require('./cordova'), 3 | 'cordova:build': require('./build'), 4 | 'cordova:open': require('./open'), 5 | 'cordova:prepare': require('./prepare'), 6 | 'cordova:archive': require('./archive') 7 | }; 8 | -------------------------------------------------------------------------------- /node-tests/helpers/mocks.js: -------------------------------------------------------------------------------- 1 | var noop = require('./noop'); 2 | 3 | exports.ui = { 4 | write: noop, 5 | pleasantProgress: { 6 | write: noop, 7 | start: noop, 8 | stop: noop, 9 | clear: noop, 10 | } 11 | } 12 | 13 | exports.Promise = { 14 | denodeify: function(fn) { return fn; } 15 | } 16 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/app/routes/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | actions: { 5 | back: function() { 6 | history.back(); 7 | }, 8 | 9 | openLink: function(url) { 10 | window.open(url, '_system'); 11 | } 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /public/inject-cordova.js: -------------------------------------------------------------------------------- 1 | // ember-cli-cordova 2 | (function() { 3 | var platform = navigator.platform; 4 | if (platform.match(/(ipad|iphone|ipod|android)/i)) { 5 | var script = document.createElement('script'); 6 | script.setAttribute('src', 'cordova.js'); 7 | document.head.appendChild(script); 8 | } 9 | })() 10 | -------------------------------------------------------------------------------- /addon/initializers/in-app-livereload.js: -------------------------------------------------------------------------------- 1 | import redirect from '../utils/redirect'; 2 | 3 | export var initialize = function(app, config) { 4 | var url = config.cordova.emberUrl || 'http://localhost:4200'; 5 | return redirect(url); 6 | }; 7 | 8 | export default { 9 | name: 'cordova:in-app-livereload', 10 | initialize: initialize 11 | }; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | !/dist/.keep 6 | /tmp 7 | 8 | # dependencies 9 | /node_modules 10 | /bower_components/* 11 | 12 | # misc 13 | /.sass-cache 14 | /connect.lock 15 | /coverage/* 16 | /libpeerconnection.log 17 | npm-debug.log 18 | testem.log 19 | .idea 20 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-tap-highlight-color: rgba(0,0,0,0); 3 | -webkit-tap-highlight-color: transparent; 4 | box-sizing: border-box; 5 | -webkit-touch-callout: none; 6 | -webkit-user-select: none; 7 | input, textarea { 8 | -webkit-touch-callout: auto; 9 | -webkit-user-select: auto; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/ui/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var PleasantProgress = require('pleasant-progress'); 4 | 5 | module.exports = { 6 | pleasantProgress: new PleasantProgress(), 7 | start: function(msg) { 8 | this.pleasantProgress.stop(true); 9 | this.pleasantProgress.start(msg) 10 | }, 11 | write: function(msg) { 12 | this.pleasantProgress.stream.write(msg); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/tasks/add-platforms.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runCommand = require('../utils/run-command'); 4 | var path = require('path'); 5 | 6 | module.exports = function(project, options) { 7 | var command = 'cordova platforms add ' + options.platform; 8 | 9 | return runCommand(command, 'Adding ' + options.platform + ' platform to cordova', { 10 | cwd: path.join(project.root, 'cordova') 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /lib/tasks/create-cordova-project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runCommand = require('../utils/run-command'); 4 | var path = require('path'); 5 | 6 | module.exports = function(project) { 7 | var config = project.cordovaConfig; 8 | var command = 'cordova create cordova ' + config.id + ' ' + config.name; 9 | 10 | return runCommand(command, 'Creating Cordova project', { 11 | cwd: project.root 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | var Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | this.resource('nav-bar', function() { 10 | this.route('page-1'); 11 | this.route('options-from-model'); 12 | this.route('should-reset'); 13 | }); 14 | }); 15 | 16 | export default Router; 17 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/nav-bar/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import NavBarMixin from 'ember-cli-cordova/mixins/routes/nav-bar'; 3 | 4 | export default Ember.Route.extend(NavBarMixin, { 5 | nav: { 6 | controller: 'nav-bar', 7 | title: { 8 | text: 'Index' 9 | }, 10 | leftButton: { 11 | text: 'iLeft' 12 | }, 13 | rightButton: { 14 | text: 'iRight' 15 | }, 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/nav-bar/page-1.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import NavBarMixin from 'ember-cli-cordova/mixins/routes/nav-bar'; 3 | 4 | export default Ember.Route.extend(NavBarMixin, { 5 | nav: { 6 | controller: 'nav-bar', 7 | title: { 8 | text: 'Page 1' 9 | }, 10 | leftButton: { 11 | text: 'pLeft' 12 | }, 13 | rightButton: { 14 | text: 'pRight' 15 | }, 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /node-tests/helpers/stub.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function stub(obj, name, value) { 4 | var original = obj[name]; 5 | 6 | obj[name] = function() { 7 | obj[name].called++; 8 | obj[name].calledWith.push(arguments); 9 | return value; 10 | }; 11 | 12 | obj[name].restore = function() { 13 | obj[name] = original; 14 | }; 15 | 16 | obj[name].called = 0; 17 | obj[name].calledWith = []; 18 | 19 | return obj[name]; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/tasks/update-config-xml-version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | var versionRegex = /(version=\")[\d.]+(\")/; 6 | 7 | module.exports = function(version, project) { 8 | var message = 'Update config.xml with version ' + version; 9 | var cordovaPath = path.join(project.root, 'cordova'); 10 | 11 | return require('./modify-xml')(message, cordovaPath, function(xml) { 12 | return this.xmlReplace(versionRegex, version, xml); 13 | }); 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /lib/tasks/cordova.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var runCommand = require('../utils/run-command'); 5 | 6 | module.exports = function(rawArgs, project) { 7 | 8 | var joinedArgs = rawArgs.join(' '); 9 | var cdvCommand = 'cordova ' + joinedArgs; 10 | 11 | var msg = "Running 'cordova " + joinedArgs + "'"; 12 | 13 | return function(){ 14 | return runCommand(cdvCommand, msg, { 15 | cwd: path.join(project.root, 'cordova') 16 | })(); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | 8 | document.write('
'); 9 | 10 | QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'}); 11 | var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible'; 12 | document.getElementById('ember-testing-container').style.visibility = containerVisibility; 13 | -------------------------------------------------------------------------------- /node-tests/fixtures/project/cordova/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExampleApp 4 | 5 | A sample Apache Cordova application that responds to the deviceready event. 6 | 7 | 8 | Apache Cordova Team 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let application; 7 | 8 | let attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(() => { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /* global require, module */ 3 | var EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | var app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /app/templates/cdv-generic-nav-bar.hbs: -------------------------------------------------------------------------------- 1 | {{#if nav.leftButton.text}} 2 | 8 | {{/if}} 9 | 10 | {{#if nav.title.text}} 11 |

12 | {{nav.title.text}} 13 |

14 | {{/if}} 15 | 16 | {{#if nav.rightButton.text}} 17 | 23 | {{/if}} 24 | -------------------------------------------------------------------------------- /lib/tasks/verify-dist.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runCommand = require('../utils/run-command'); 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | var Promise = require('../ext/promise'); 7 | 8 | module.exports = function(project) { 9 | return function() { 10 | var distPath = path.join(project.root, 'dist'); 11 | 12 | if(fs.existsSync(distPath)) { 13 | return Promise.resolve(); 14 | } else { 15 | return runCommand('ember build', 'Building ember app since dist/ dir doesn\'t exist yet', { 16 | cwd: project.root 17 | })(); 18 | } 19 | 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import startApp from '../helpers/start-app'; 3 | import destroyApp from '../helpers/destroy-app'; 4 | 5 | export default function(name, options = {}) { 6 | module(name, { 7 | beforeEach() { 8 | this.application = startApp(); 9 | 10 | if (options.beforeEach) { 11 | options.beforeEach.apply(this, arguments); 12 | } 13 | }, 14 | 15 | afterEach() { 16 | destroyApp(this.application); 17 | 18 | if (options.afterEach) { 19 | options.afterEach.apply(this, arguments); 20 | } 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/helpers/describe-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import startApp from './start-app'; 3 | 4 | export default function(name, callback) { 5 | return describe(name, function() { 6 | before(function() { 7 | this.app = startApp(); 8 | }); 9 | 10 | after(function() { 11 | Ember.run(this.app, 'destroy'); 12 | }); 13 | 14 | lazy('store', function() { 15 | return this.app.__container__.lookup('store:main'); 16 | }); 17 | 18 | helper('lookupController', function(name) { 19 | return this.app.__container__.lookup('controller:' + name); 20 | }); 21 | 22 | callback.call(this); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.css] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.html] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.md] 33 | trim_trailing_whitespace = false 34 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise" 6 | ], 7 | "browser": true, 8 | "boss": true, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "eqeqeq": true, 13 | "evil": true, 14 | "forin": false, 15 | "immed": false, 16 | "laxbreak": false, 17 | "newcap": true, 18 | "noarg": true, 19 | "noempty": false, 20 | "nonew": false, 21 | "nomen": false, 22 | "onevar": false, 23 | "plusplus": false, 24 | "regexp": false, 25 | "undef": true, 26 | "sub": true, 27 | "strict": false, 28 | "white": false, 29 | "eqnull": true, 30 | "esnext": true, 31 | "unused": true, 32 | "moz": true 33 | } 34 | -------------------------------------------------------------------------------- /lib/utils/string.js: -------------------------------------------------------------------------------- 1 | // https://github.com/emberjs/ember.js/blob/v1.5.0/packages/ember-runtime/lib/system/string.js 2 | 'use strict'; 3 | 4 | var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); 5 | var _s = require('underscore.string'); 6 | 7 | module.exports = { 8 | decamelize: function(str) { 9 | return str ? str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase() : ''; 10 | }, 11 | dasherize: function(str) { 12 | return str ? _s.dasherize(str).replace(/^\-/, '') : ''; 13 | }, 14 | classify: function(str) { 15 | // Have to humanize first so that 'MyApp' doesnt turn into 'Myapp' 16 | return str ? _s.classify(_s.humanize(str)) : ''; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /node-tests/unit/models/project-with-config-test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var projectWithConfig = require('../../../lib/models/project-with-config'); 4 | 5 | describe('Model - ProjectWithConfig', function() { 6 | var project; 7 | 8 | beforeEach(function() { 9 | project = { 10 | root: path.join(__dirname, '..', '..', 'fixtures/project'), 11 | name: function() {} 12 | } 13 | }); 14 | 15 | it('adds the ember cordova config instance', function() { 16 | project = projectWithConfig(project); 17 | expect(project.cordovaConfig.id).to.equal('com.example.app'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/initializers/in-app-livereload.js: -------------------------------------------------------------------------------- 1 | /* globals cordova */ 2 | 3 | import config from '../config/environment'; 4 | import reloadInitializer from 'ember-cli-cordova/initializers/in-app-livereload'; 5 | 6 | var inAppReload = reloadInitializer.initialize; 7 | 8 | export var initialize = function(app) { 9 | if(typeof cordova === 'undefined' || 10 | config.environment !== 'development' || 11 | (config.cordova && 12 | (!config.cordova.liveReload || !config.cordova.liveReload.enabled))) { 13 | return; 14 | } 15 | 16 | return inAppReload(app, config); 17 | }; 18 | 19 | export default { 20 | name: 'cordova:in-app-livereload', 21 | initialize: initialize 22 | }; 23 | -------------------------------------------------------------------------------- /lib/commands/open.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var defaultPlatform = require('../utils/default-platform'); 5 | 6 | module.exports = { 7 | name: 'cordova:open', 8 | aliases: ['cdv:open'], 9 | description: 'Open the native platform project with the default or specified application', 10 | works: 'insideProject', 11 | 12 | availableOptions: [ 13 | { name: 'platform', type: String }, 14 | { name: 'application', type: String} 15 | ], 16 | 17 | run: function(options) { 18 | var platform = options.platform || defaultPlatform(this.project); 19 | return require('../tasks/open')(this.project, platform, options.application)(); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /tests/dummy/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": { 3 | "document": true, 4 | "window": true, 5 | "-Promise": true, 6 | "DummyENV": true 7 | }, 8 | "browser" : true, 9 | "boss" : true, 10 | "curly": true, 11 | "debug": false, 12 | "devel": true, 13 | "eqeqeq": true, 14 | "evil": true, 15 | "forin": false, 16 | "immed": false, 17 | "laxbreak": false, 18 | "newcap": true, 19 | "noarg": true, 20 | "noempty": false, 21 | "nonew": false, 22 | "nomen": false, 23 | "onevar": false, 24 | "plusplus": false, 25 | "regexp": false, 26 | "undef": true, 27 | "sub": true, 28 | "strict": false, 29 | "white": false, 30 | "eqnull": true, 31 | "esnext": true, 32 | "unused": true 33 | } 34 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | 12 | 13 | 14 | 15 | {{content-for 'head-footer'}} 16 | 17 | 18 | {{content-for 'body'}} 19 | 20 | 21 | 22 | 23 | {{content-for 'body-footer'}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/commands/cordova.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var chalk = require('chalk'); 5 | 6 | module.exports = { 7 | name: 'cordova', 8 | aliases: ['cdv'], 9 | description: 'Passes commands(plugin(s), platform(s), run, emulate) and arguments to the cordova command', 10 | works: 'insideProject', 11 | allowedCordovaCommands: [ 12 | 'plugin', 'plugins', 'platform', 'platforms', 'run', 'emulate' 13 | ], 14 | 15 | validateAndRun: function(rawArgs) { 16 | if(this.allowedCordovaCommands.indexOf(rawArgs[0]) > -1) { 17 | return this.run({}, rawArgs); 18 | } 19 | }, 20 | 21 | run: function(options, rawArgs) { 22 | return require('../tasks/cordova')(rawArgs, this.project)(); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /addon/mixins/controllers/nav-bar.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Mixin.create({ 4 | nav: { 5 | title: { }, 6 | leftButton: { }, 7 | rightButton: { } 8 | }, 9 | 10 | actions: { 11 | leftButton: function() { 12 | var leftAction = this.get('nav.leftButton.action'); 13 | 14 | if(leftAction) { 15 | leftAction(); 16 | } 17 | }, 18 | 19 | rightButton: function() { 20 | var rightAction = this.get('nav.rightButton.action'); 21 | 22 | if(rightAction) { 23 | rightAction(); 24 | } 25 | }, 26 | 27 | resetNavBar: function() { 28 | this.set('nav', { 29 | title: { }, 30 | leftButton: { }, 31 | rightButton: { } 32 | }); 33 | } 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /lib/commands/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var chalk = require('chalk'); 5 | var defaultPlatform = require('../utils/default-platform'); 6 | 7 | module.exports = { 8 | name: 'cordova:build', 9 | aliases: ['cdv:build'], 10 | description: 'Build the ember and cordova project together running in the simulator or on a device', 11 | works: 'insideProject', 12 | 13 | availableOptions: [ 14 | { name: 'environment', type: String, default: 'development' }, 15 | { name: 'platform', type: String } 16 | ], 17 | 18 | run: function(options) { 19 | var platform = options.platform || defaultPlatform(this.project); 20 | return require('../tasks/build')(options.environment, platform, this.project)(); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /node-tests/helpers/_helper.js: -------------------------------------------------------------------------------- 1 | global.expect = require('chai').expect; 2 | global.sinon = require('sinon'); 3 | 4 | // be sure to not have any env variable set 5 | delete process.env.EMBER_CLI_CORDOVA; 6 | 7 | // Requiring a relative path will need to be relative to THIS file path 8 | global.proxyquire = require('proxyquire'); 9 | 10 | global.Promise = require('../../lib/ext/promise'); 11 | global.noop = require('./noop'); 12 | 13 | global.resolveFn = function() { return Promise.resolve() }; 14 | 15 | global.newProject = function() { 16 | return { 17 | cordovaConfig: { 18 | id: 'com.poetic.test-app', 19 | name: 'TestApp' 20 | }, 21 | root: 'project-root', 22 | config: function (){ 23 | return { 24 | cordova: {} 25 | }; 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/add-platforms-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Add Platforms', function() { 2 | it('creates the proper command', function() { 3 | var addPlatforms = proxyquire('../../lib/tasks/add-platforms', { 4 | '../utils/run-command': function(command) { 5 | expect(command).to.contain('platforms add some-platform'); 6 | } 7 | }); 8 | 9 | return addPlatforms({root: 'test'}, {platform: 'some-platform'}); 10 | }); 11 | 12 | it('executes command in cordova directory', function() { 13 | var addPlatforms = proxyquire('../../lib/tasks/add-platforms', { 14 | '../utils/run-command': function(_, __, options) { 15 | expect(options.cwd).to.equal('test/cordova'); 16 | } 17 | }); 18 | 19 | return addPlatforms({root: 'test'}, {platform: 'some-platform'}); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/models/project-with-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var stringUtils = require('../utils/string'); 4 | 5 | var getComId = function(project) { 6 | 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | 10 | var configPath = path.join(project.root, 'cordova', 'config.xml'); 11 | var configFile = fs.readFileSync(configPath, { encoding: 'utf-8'}); 12 | 13 | var idRegex = /id=\"([\w\-\.]+)\"/; 14 | var matches = configFile.match(idRegex); 15 | 16 | if(matches.length) { 17 | return matches[1]; 18 | } 19 | 20 | throw new Error('Unable to find an id within your cordova/config.xml'); 21 | } 22 | 23 | module.exports = function(project, id) { 24 | project.cordovaConfig = { 25 | name: stringUtils.classify(project.name()), 26 | id: id || getComId(project) 27 | }; 28 | 29 | return project; 30 | }; 31 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/verify-dist-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Verify Dist', function() { 2 | var project; 3 | beforeEach(function() { 4 | project = newProject(); 5 | }); 6 | 7 | it('resolves when path exists', function() { 8 | var verifyDist = proxyquire('../../lib/tasks/verify-dist', { 9 | 'fs': { 10 | existsSync: function() { return true; } 11 | } 12 | }); 13 | 14 | return verifyDist(project)().then(function() { 15 | expect(true).to.be.ok; 16 | }); 17 | }); 18 | 19 | it('runs ember build when it doesn\'t exist', function() { 20 | var verifyDist = proxyquire('../../lib/tasks/verify-dist', { 21 | '../utils/run-command': function(command) { 22 | expect(command).to.eql('ember build'); 23 | } 24 | }); 25 | 26 | return verifyDist(project); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/update-config-xml-version-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | describe('Tasks - Update config xml version', function() { 4 | var project; 5 | beforeEach(function() { 6 | project = { 7 | root: path.join(__dirname, '..', '..', 'fixtures/project') 8 | } 9 | }); 10 | 11 | it('updates version and writes it', function() { 12 | var update = proxyquire('../../lib/tasks/update-config-xml-version', { 13 | './modify-xml': proxyquire('../../lib/tasks/modify-xml', { 14 | 'fs': { 15 | writeFileSync: function(path, xml) { 16 | expect(path).to.eql(project.root + '/cordova/config.xml'); 17 | expect(xml).to.match(/\sversion=\"0.1.0"\s/); 18 | } 19 | } 20 | }) 21 | }); 22 | 23 | return update('0.1.0', project)(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /lib/commands/prepare.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var linkEnv = require('../tasks/link-environment'); 3 | var runCommand = require('../utils/run-command'); 4 | var Promise = require('../ext/promise'); 5 | 6 | module.exports = { 7 | name: 'cordova:prepare', 8 | aliases: ['cdv:prepare'], 9 | description: 'Needed after cloning or copying a project.', 10 | works: 'insideProject', 11 | 12 | run: function() { 13 | var installDeps = runCommand('npm install && bower install', 'Installing npm and bower dependencies', { 14 | cwd: this.project.root 15 | }); 16 | 17 | // Because of this being parallel. It breaks the logging and it looks like 18 | // 19 | // Symlinking ember dir to cordova www... 20 | // Installing npm and bower dependencies...donedone 21 | return Promise.all([linkEnv(this.project)(), installDeps()]); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/cordova-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Cordova', function() { 2 | it('creates a proper command', function() { 3 | var cordova = proxyquire('../../lib/tasks/cordova', { 4 | '../utils/run-command': function(command, msg, options){ 5 | expect(command).to.match(/cordova plugins add org\.apache\.test$/); 6 | return noop; 7 | }, 8 | }); 9 | 10 | return cordova(['plugins', 'add', 'org.apache.test'], { root: 'test' })(); 11 | }); 12 | 13 | it('executes in proper directory', function() { 14 | var cordova = proxyquire('../../lib/tasks/cordova', { 15 | '../utils/run-command': function(command, msg, options){ 16 | expect(options.cwd).to.match(/test\/cordova$/); 17 | return noop; 18 | }, 19 | }); 20 | 21 | return cordova(['plugins', 'add', 'org.apache.test'], { root: 'test' })(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /lib/tasks/open.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('../ext/promise'); 4 | var path = require('path'); 5 | var runCommand = require('../utils/run-command'); 6 | var getOpenCommand = require('../utils/open'); 7 | 8 | module.exports = function(project, platform, application) { 9 | var projectPath, command; 10 | if (platform === 'ios') { 11 | projectPath = path.join(project.root, 'cordova', 'platforms/ios/*.xcodeproj'); 12 | } else if (platform === 'android') { 13 | projectPath = path.join(project.root, 'cordova', 'platforms/android/.project'); 14 | } else { 15 | return Promise.reject(new Error('The ' + platform + ' platform is not supported. Please use "ios" or "android"')); 16 | } 17 | 18 | var command = getOpenCommand(projectPath, application); 19 | 20 | return runCommand(command, 'Opening ' + platform + ' project with the default application'); 21 | }; 22 | -------------------------------------------------------------------------------- /lib/tasks/link-environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('../ext/promise'); 4 | var fs = require('fs-extra'); 5 | var symlink = Promise.denodeify(fs.symlink); 6 | var remove = Promise.denodeify(fs.remove); 7 | var path = require('path'); 8 | var chalk = require('chalk'); 9 | var verifyDist = require('./verify-dist'); 10 | 11 | module.exports = function(project){ 12 | if(!project) { 13 | throw new Error('A project must be passed into this function'); 14 | } 15 | 16 | var cordovaPath = path.join(project.root, 'cordova'); 17 | var wwwPath = path.join(cordovaPath, 'www'); 18 | 19 | return function() { 20 | // allows us to do a relative symlink 21 | process.chdir(cordovaPath); 22 | 23 | return remove(wwwPath) 24 | .then(symlink.bind(this, '../dist', 'www', 'dir')) 25 | .then(verifyDist(project)); 26 | }; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /lib/commands/archive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var projectWithConfig = require('../models/project-with-config'); 5 | 6 | module.exports = { 7 | name: 'cordova:archive', 8 | aliases: ['cdv:archive'], 9 | description: 'Build project and create xcode archive. If the tag or commit options are present they will be performed after archiving.', 10 | works: 'insideProject', 11 | 12 | anonymousOptions: [ 13 | '' 14 | ], 15 | 16 | availableOptions: [ 17 | { name: 'environment', type: String, default: 'staging' }, 18 | { name: 'tag', type: Boolean, default: false }, 19 | { name: 'commit', type: Boolean, default: false } 20 | ], 21 | 22 | run: function(options, rawArgs) { 23 | projectWithConfig(this.project); 24 | var version = rawArgs[0]; 25 | 26 | return require('../tasks/archive')(version, options, this.project)(); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /lib/tasks/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runCommand = require('../utils/run-command'); 4 | var path = require('path'); 5 | var linkEnv = require('../tasks/link-environment'); 6 | 7 | module.exports = function(env, platform, project) { 8 | var emberCommand = 'ember build --environment ' + env; 9 | 10 | var emberMsg = 'Building ember project for environment ' + env; 11 | var emberBuild = runCommand(emberCommand, emberMsg, { 12 | cwd: project.root 13 | }); 14 | 15 | var cdvCommand = 'cordova build ' + platform; 16 | 17 | if (env !== 'development') { 18 | cdvCommand += ' --release' 19 | } 20 | 21 | var cdvMsg = 'Building cordova project for platform ' + platform; 22 | var cdvBuild = runCommand(cdvCommand, cdvMsg, { 23 | cwd: path.join(project.root, 'cordova') 24 | }); 25 | 26 | return function(){ 27 | return linkEnv(project)().then(emberBuild).then(cdvBuild); 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/create-cordova-project-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Create cordova project', function() { 2 | var project; 3 | beforeEach(function() { 4 | project = newProject(); 5 | }); 6 | 7 | it('creates the proper command', function() { 8 | var createProject = proxyquire('../../lib/tasks/create-cordova-project', { 9 | '../utils/run-command': function(command) { 10 | expect(command).to.eql('cordova create cordova com.poetic.test-app TestApp'); 11 | return resolveFn; 12 | } 13 | }); 14 | 15 | return createProject(project)(); 16 | }); 17 | 18 | it('should execute in proper folder', function() { 19 | var createProject = proxyquire('../../lib/tasks/create-cordova-project', { 20 | '../utils/run-command': function(_, _, options) { 21 | expect(options.cwd).to.equal('project-root'); 22 | return resolveFn; 23 | } 24 | }); 25 | 26 | return createProject(project)(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /lib/tasks/modify-xml.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var chalk = require('chalk'); 5 | var path = require('path'); 6 | var ui = require('../ui'); 7 | var Promise = require('../ext/promise'); 8 | 9 | // Used as the context of this for the replaceFn so it can be used 10 | var replaceObject = { 11 | xmlReplace: function(regex, value, xml) { 12 | return xml.replace(regex, '$1' + value + '$2'); 13 | } 14 | }; 15 | 16 | module.exports = function(message, root, replaceFn) { 17 | return function modifyXml() { 18 | return new Promise(function(resolve, reject){ 19 | try { 20 | var configPath = path.join(root, 'config.xml'); 21 | 22 | ui.start(chalk.green(message)); 23 | 24 | var xml = fs.readFileSync(configPath, { encoding: 'utf8' }); 25 | 26 | xml = replaceFn.call(replaceObject, xml); 27 | 28 | fs.writeFileSync(configPath, xml); 29 | resolve(); 30 | 31 | } catch(e) { 32 | reject(e); 33 | } 34 | }); 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /lib/tasks/update-config-xml.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | // Match the outer tags so we can match and reinsert them with a String#replace 6 | var idRegex = /(id=\")[\w\.]+(\")/; 7 | var nameRegex = /()\w+(<\/name>)/; 8 | var endWidgetRegex = /(.*)(<\/widget>)/; 9 | 10 | var preferences = [ 11 | // haha, 4 spaces at the beginning 12 | ' ' 13 | ]; 14 | 15 | module.exports = function(project) { 16 | var config = project.cordovaConfig; 17 | var message = 'Update config.xml with your project settings'; 18 | var cordovaPath = path.join(project.root, 'cordova'); 19 | 20 | return require('./modify-xml')(message, cordovaPath, function(xml) { 21 | xml = this.xmlReplace(idRegex, config.id, xml); 22 | xml = this.xmlReplace(nameRegex, config.name, xml); 23 | 24 | // add preference tag(s) 25 | xml = this.xmlReplace(endWidgetRegex, preferences.join('\n') + '\n', xml); 26 | 27 | return xml; 28 | }); 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | #### I am getting `Current working directory is not a Cordova-based project.` when I run a cordova command 4 | 5 | If you are running a cli command, make sure the dist directory exists. You can 6 | run `ember build` to create it if it doesnt. You can also try to run `ember 7 | cordova:prepare` 8 | 9 | #### When running `ember cordova:archive` command I get an Xcode build error saying the scheme doesnt exist 10 | 11 | Error example: 12 | 13 | ``` 14 | ld[10658:1007] WARNING: Timed out waiting for /"runContextManager.runContexts" (10.000125 seconds elapsed) 16 | xcodebuild: error: The project 'MyApp' does not contain a scheme named 'MyApp'. 17 | ``` 18 | 19 | This is caused by not having opened the project in Xcode before. It 20 | automatically generates some info it needs to archive the project. To fix this, 21 | run `ember cordova:open` and let it open in Xcode. After you've done this once you 22 | can just run `ember cordova:archive` command again and it shouldn't give you any more 23 | trouble. 24 | 25 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/nav-bar/options-from-model.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import NavBarMixin from 'ember-cli-cordova/mixins/routes/nav-bar'; 3 | 4 | export default Ember.Route.extend(NavBarMixin, { 5 | nav: { 6 | controller: 'nav-bar', 7 | title: { 8 | text: function(model) { 9 | return model.get('title'); 10 | } 11 | }, 12 | 13 | leftButton: { 14 | text: function(model) { 15 | return model.get('leftButton'); 16 | }, 17 | icon: function(model) { 18 | return model.get('leftClass'); 19 | } 20 | }, 21 | 22 | rightButton: { 23 | text: function(model) { 24 | return model.get('rightButton'); 25 | }, 26 | icon: function(model) { 27 | return model.get('rightClass'); 28 | } 29 | } 30 | }, 31 | 32 | model: function() { 33 | return Ember.Object.create({ 34 | title: 'modelOption', 35 | leftButton: 'modelLeft', 36 | leftClass: 'leftClass', 37 | rightButton: 'modelRight', 38 | rightClass: 'rightClass' 39 | }); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Jake Craige 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/tasks/update-config-xml-android-version-code.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var Promise = require('../ext/promise'); 6 | 7 | var versionCodeRegex = /(android-versionCode=\")[\d.]+(\")/; 8 | var versionCodeMatch = /android-versionCode=\"([\d.])+\"/; 9 | 10 | module.exports = function(project) { 11 | return new Promise(function(resolve, reject){ 12 | try { 13 | var cordovaPath = path.join(project.root, 'cordova'); 14 | var configPath = path.join(cordovaPath, 'config.xml'); 15 | var xml = fs.readFileSync(configPath, { encoding: 'utf8' }); 16 | 17 | var match = xml.match(versionCodeMatch); 18 | if(match) { 19 | var versionCode = (parseInt(match[1], 10)) + 1; 20 | var message = 'Update config.xml with android-versionCode ' + versionCode; 21 | 22 | return require('./modify-xml')(message, cordovaPath, function(xml) { 23 | return this.xmlReplace(versionCodeRegex, versionCode, xml); 24 | })().then(resolve, reject); 25 | } else { 26 | reject(); 27 | } 28 | } catch (e) { 29 | reject(e); 30 | } 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'hash', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | 16 | APP: { 17 | // Here you can pass flags/options to your application instance 18 | // when it is created 19 | } 20 | }; 21 | 22 | if (environment === 'development') { 23 | // ENV.APP.LOG_RESOLVER = true; 24 | ENV.APP.LOG_ACTIVE_GENERATION = true; 25 | // ENV.APP.LOG_TRANSITIONS = true; 26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 27 | ENV.APP.LOG_VIEW_LOOKUPS = true; 28 | } 29 | 30 | if (environment === 'test') { 31 | // Testem prefers this... 32 | ENV.baseURL = '/'; 33 | ENV.locationType = 'auto'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | } 41 | 42 | if (environment === 'production') { 43 | 44 | } 45 | 46 | return ENV; 47 | }; 48 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/update-config-xml-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var uiMock = { start: noop }; 3 | 4 | var project; 5 | describe('Tasks - Update config xml', function() { 6 | beforeEach(function() { 7 | project = { 8 | cordovaConfig: { 9 | id: 'com.poetic.test-app', 10 | name: 'TestApp' 11 | }, 12 | root: path.join(__dirname, '..', '..', 'fixtures/project') 13 | } 14 | }); 15 | 16 | it('updates id and name', function() { 17 | return proxyUpdate(function(xml) { 18 | expect(xml).to.match(/\sid=\"com\.poetic\.test\-app"\s/); 19 | expect(xml).to.match(/TestApp<\/name>/); 20 | }); 21 | }); 22 | 23 | it('adds DisallowOverscroll preference', function() { 24 | return proxyUpdate(function(xml) { 25 | expect(xml).to.match(//); 26 | }); 27 | }); 28 | }); 29 | 30 | function proxyUpdate(callback) { 31 | var update = proxyquire('../../lib/tasks/update-config-xml', { 32 | './modify-xml': proxyquire('../../lib/tasks/modify-xml', { 33 | 'fs': { 34 | writeFileSync: function(path, xml) { 35 | callback(xml); 36 | } 37 | }, 38 | '../ui': uiMock 39 | }) 40 | }); 41 | 42 | return update(project)(); 43 | } 44 | -------------------------------------------------------------------------------- /blueprints/cordova-init/files/gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components/* 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log 17 | testem.log 18 | 19 | # iOS 20 | cordova/platforms/ios/build/ 21 | cordova/platforms/ios/www/ 22 | cordova/platforms/ios/cordova/console.log 23 | *.xcuserdatad 24 | 25 | # android 26 | cordova/platforms/android/assets/www 27 | cordova/platforms/android/bin 28 | cordova/platforms/android/gen 29 | cordova/platforms/android/local.properties 30 | cordova/platforms/android/ant-build 31 | cordova/platforms/android/ant-gen 32 | cordova/platforms/android/CordovaLib/ant-build 33 | cordova/platforms/android/CordovaLib/ant-gen 34 | cordova/platforms/android/CordovaLib/bin 35 | cordova/platforms/android/CordovaLib/gen 36 | cordova/platforms/android/CordovaLib/local.properties 37 | 38 | # wp8 39 | cordova/platforms/wp8/bin 40 | cordova/platforms/wp8/obj 41 | cordova/platforms/wp8/www 42 | cordova/platforms/wp8/.staging 43 | cordova/platforms/wp8/*.suo 44 | cordova/platforms/wp8/*.csproj.user 45 | 46 | #browser 47 | cordova/platforms/browser/build 48 | cordova/platforms/browser/www 49 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var chalk = require('chalk'); 3 | var Promise = require('../../lib/ext/promise'); 4 | var stringUtils = require('../../lib/utils/string'); 5 | var runCommand = require('../../lib/utils/run-command'); 6 | 7 | module.exports = { 8 | // Allows the generator to not require an entity name 9 | normalizeEntityName: function(entityName) { 10 | return entityName; 11 | }, 12 | 13 | locals: function(options) { 14 | var name = options.project.pkg.name; 15 | 16 | return { 17 | namespace: stringUtils.classify(name), 18 | modulePrefix: stringUtils.dasherize(name) 19 | } 20 | }, 21 | 22 | addPluginToProject: function(name) { 23 | var ui = this.ui; 24 | 25 | return runCommand('cordova plugin add ' + name, null, { 26 | cwd: path.join(this.project.root, 'cordova') 27 | })().then(function() { 28 | if (ui) { 29 | ui.writeLine(' ' + chalk.green('install plugin') + ' ' + name); 30 | } 31 | }); 32 | }, 33 | 34 | afterInstall: function() { 35 | return Promise.all([ 36 | this.addPackageToProject('broccoli-sass'), 37 | this.addPackageToProject('liquid-fire'), 38 | this.addPackageToProject('ember-gestures') 39 | ]); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /lib/tasks/post-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runCommand = require('../utils/run-command'); 4 | var defaultPlatform = require('../utils/default-platform'); 5 | var path = require('path'); 6 | var chalk = require('chalk'); 7 | var ui = require('../ui'); 8 | var Promise = require('../ext/promise'); 9 | 10 | function createCommand(project, options) { 11 | var platform = options.platform || defaultPlatform(project); 12 | var command = 'cordova build ' + platform; 13 | 14 | if (options.emulate) { 15 | command += ' && cordova emulate ' + platform; 16 | 17 | if (options.emulateTarget) { 18 | if (options.emulateTarget[platform]) { 19 | command += ' --target="' + options.emulateTarget[platform] + '"'; 20 | } 21 | } 22 | } 23 | 24 | return runCommand(command, null, { 25 | cwd: path.join(project.root, 'cordova') 26 | }); 27 | } 28 | 29 | module.exports = function(project, options) { 30 | if (!options.rebuildOnChange) { 31 | return function() {}; 32 | } 33 | 34 | return function() { 35 | var rebuild = createCommand(project, options)(); 36 | 37 | rebuild.then(function() { 38 | ui.write(chalk.green('Cordova build successful.\n')); 39 | }); 40 | 41 | return Promise.resolve(); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /lib/utils/open.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | This file needs to be removed when issue https://github.com/pwnall/node-open/issues/30 4 | is resolved, original file can be seen here: 5 | https://github.com/pwnall/node-open/blob/0c3ad272bfbc163cce8806e64630c623a9cfd8f4/lib/open.js 6 | */ 7 | module.exports = function(target, appName) { 8 | var opener; 9 | 10 | switch (process.platform) { 11 | case 'darwin': 12 | if (appName) { 13 | opener = 'open -a "' + escape(appName) + '"'; 14 | } else { 15 | opener = 'open'; 16 | } 17 | break; 18 | case 'win32': 19 | // if the first parameter to start is quoted, it uses that as the title 20 | // so we pass a blank title so we can quote the file we are opening 21 | if (appName) { 22 | opener = 'start "" "' + escape(appName) + '"'; 23 | } else { 24 | opener = 'start'; 25 | } 26 | break; 27 | default: 28 | if (appName) { 29 | opener = escape(appName); 30 | } else { 31 | // use Portlands xdg-open everywhere else 32 | opener = 'xdg-open'; 33 | } 34 | break; 35 | } 36 | 37 | if (process.env.SUDO_USER) { 38 | opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener; 39 | } 40 | 41 | return opener + ' ' + target; 42 | } 43 | -------------------------------------------------------------------------------- /lib/utils/run-command.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('../ext/promise'); 4 | var exec = require('child_process').exec; 5 | var chalk = require('chalk'); 6 | var ui = require('../ui'); 7 | var defaults = require('lodash').defaults; 8 | 9 | module.exports = function runCommand(command, startedMsg, options) { 10 | if(options == null) { 11 | options = {} 12 | } 13 | 14 | return function() { 15 | if(startedMsg) { 16 | ui.start(chalk.green(startedMsg)); 17 | } 18 | 19 | options = defaults(options, { 20 | maxBuffer: 5000 * 1024 21 | }); 22 | 23 | return new Promise(function(resolve, reject) { 24 | exec(command, options, function(err, stdout, stderr) { 25 | ui.write('\n'); 26 | 27 | if (stdout && stdout.length) { 28 | ui.write(stdout); 29 | } 30 | 31 | if (stderr && stderr.length) { 32 | ui.write(stderr); 33 | } 34 | 35 | if (err) { 36 | return reject(commandError(command, err)); 37 | } 38 | 39 | resolve(stdout); 40 | }); 41 | }); 42 | }; 43 | }; 44 | 45 | function commandError(command, err) { 46 | ui.write(chalk.red('\nError thrown while running shell command: "' + command + '"\n')); 47 | if(err.stack) { 48 | ui.write(err.stack); 49 | } else { 50 | ui.write(err); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /blueprints/cordova-init/index.js: -------------------------------------------------------------------------------- 1 | var projectWithConfig = require('../../lib/models/project-with-config'); 2 | var Promise = require('../../lib/ext/promise'); 3 | var stringUtils = require('../../lib/utils/string'); 4 | var defaultPlatform = require('../../lib/utils/default-platform'); 5 | 6 | module.exports = { 7 | locals: function(options) { 8 | var name = options.project.pkg.name; 9 | 10 | return { 11 | namespace: stringUtils.classify(name), 12 | modulePrefix: stringUtils.dasherize(name) 13 | } 14 | }, 15 | 16 | afterInstall: function(options) { 17 | this.options = options.entity.options; 18 | this.options.platform = options.platform || defaultPlatform(this.project); 19 | 20 | projectWithConfig(this.project, options.entity.name); 21 | 22 | return this.setupCordova(); 23 | }, 24 | 25 | setupCordova: function() { 26 | var createProject = require('../../lib/tasks/create-cordova-project')(this.project); 27 | var addPlatforms = require('../../lib/tasks/add-platforms')(this.project, this.options); 28 | var updateConfig = require('../../lib/tasks/update-config-xml')(this.project); 29 | var linkEnvironment = require('../../lib/tasks/link-environment')(this.project); 30 | 31 | return createProject().then(addPlatforms).then(updateConfig).then(linkEnvironment); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | ### Configuration 2 | 3 | All configuration is currently optional. Configuration will be done in your 4 | app's `config/environment`. You need to set it up like this: 5 | 6 | ```js 7 | ENV.cordova = { 8 | // Rebuild the cordova project on file changes. Blocks the server until it's 9 | // finished. 10 | // 11 | // default: false 12 | rebuildOnChange: true, 13 | 14 | // Run the cordova emulate command after the build is finished 15 | // 16 | // default: false 17 | emulate: true, 18 | 19 | // Which emulated target to deploy to 20 | // 21 | // default: 22 | emulateTarget: { 23 | ios: "iPad-2", 24 | android: "android-20" 25 | }, 26 | 27 | // Which platform to build and/or emulate 28 | // 29 | // default: 'ios' 30 | platform: 'ios', 31 | 32 | // Which URL the ember server is running on. This is used when using 33 | // live-reload that comes with the starter kit. 34 | // 35 | // default: 'the-device-ip:4200' 36 | emberUrl: 'http://10.0.1.12:4200', 37 | 38 | // Whether or not to use liveReload on the device simulator. Requires a few 39 | // plugins to be installed that come with the starter-kit. It will cause your 40 | // app to not boot up in the browser 41 | // 42 | // default: false and iOS 43 | liveReload: { 44 | enabled: false, 45 | platform: 'ios' 46 | } 47 | }; 48 | ``` 49 | -------------------------------------------------------------------------------- /addon/services/cordova.js: -------------------------------------------------------------------------------- 1 | /* jshint esnext:true */ 2 | 3 | import Ember from 'ember'; 4 | 5 | // from https://cordova.apache.org/docs/en/4.0.0/cordova_events_events.md.html 6 | // use var because cordova/android was throwing errors re: const && strict mode 7 | var CORDOVA_EVENTS = Ember.A([ 8 | 'deviceready', 9 | 'pause', 10 | 'resume', 11 | 'backbutton', 12 | 'menubutton', 13 | 'searchbutton', 14 | 'startcallbutton', 15 | 'endcallbutton', 16 | 'volumedownbutton', 17 | 'volumeupbutton', 18 | 'batterycritical', 19 | 'batterylow', 20 | 'batterystatus', 21 | 'online', 22 | 'offline' 23 | ]); 24 | 25 | // the cordova service listens for cordova events emitted to the document, 26 | // and triggers the same events in emberland. 27 | // 28 | // subscribe to cordova events as such: 29 | // 30 | // ```javascript 31 | // export default MyEmberObject.extend({ 32 | // cordova: Ember.inject.service() 33 | // 34 | // init: function() { 35 | // cordova.on('resume', function() { console.log('i am resumed'); }); 36 | // } 37 | // }); 38 | // ``` 39 | export default Ember.Service.extend( 40 | Ember.Evented, { 41 | 42 | setEventTriggers: Ember.on('init', function() { 43 | var _this = this; 44 | 45 | CORDOVA_EVENTS.forEach(function(eventName) { 46 | Ember.$(document).on(eventName, function() { 47 | _this.trigger(eventName); 48 | }); 49 | }); 50 | }) 51 | }); 52 | -------------------------------------------------------------------------------- /blueprints/cordova-init/files/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: '<%= modulePrefix %>', 6 | environment: environment, 7 | baseURL: '/', 8 | defaultLocationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | 16 | APP: { 17 | // Here you can pass flags/options to your application instance 18 | // when it is created 19 | }, 20 | 21 | cordova: { 22 | rebuildOnChange: false, 23 | emulate: false 24 | } 25 | }; 26 | 27 | if (environment === 'development') { 28 | // ENV.APP.LOG_RESOLVER = true; 29 | ENV.APP.LOG_ACTIVE_GENERATION = true; 30 | // ENV.APP.LOG_TRANSITIONS = true; 31 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 32 | ENV.APP.LOG_VIEW_LOOKUPS = true; 33 | } 34 | 35 | if (environment === 'test') { 36 | // Testem prefers this... 37 | ENV.baseURL = '/'; 38 | ENV.locationType = 'none'; 39 | 40 | // keep test console output quieter 41 | ENV.APP.LOG_ACTIVE_GENERATION = false; 42 | ENV.APP.LOG_VIEW_LOOKUPS = false; 43 | 44 | ENV.APP.rootElement = '#ember-testing'; 45 | } 46 | 47 | if (environment === 'production') { 48 | 49 | } 50 | 51 | return ENV; 52 | }; 53 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | {{content-for 'test-head'}} 12 | 13 | 14 | 15 | 16 | 32 | 33 | {{content-for 'head-footer'}} 34 | {{content-for 'test-head-footer'}} 35 | 36 | 37 | {{content-for 'body'}} 38 | {{content-for 'test-body'}} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{content-for 'body-footer'}} 48 | {{content-for 'test-body-footer'}} 49 | 50 | 51 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/link-environment-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Link Environment', function() { 2 | var project; 3 | beforeEach(function() { 4 | project = newProject(); 5 | }); 6 | 7 | before(function() { 8 | process._chdir = process.chdir; 9 | process.chdir = noop; 10 | }); 11 | 12 | after(function() { 13 | process.chdir = process._chdir; 14 | }); 15 | 16 | it('removes the cordova/www dir', function() { 17 | var createProject = proxyquire('../../lib/tasks/link-environment', { 18 | './verify-dist': function() { return resolveFn; }, 19 | 'fs-extra': { 20 | remove: function(path, callback) { 21 | expect(path).to.eql('project-root/cordova/www'); 22 | return callback(null, true); 23 | }, 24 | symlink: function(from, to, type, _, callback) { 25 | return callback(null, true); 26 | } 27 | } 28 | }); 29 | 30 | return createProject(project)(); 31 | }); 32 | 33 | it('creates a relative dir symlink', function() { 34 | var createProject = proxyquire('../../lib/tasks/link-environment', { 35 | './verify-dist': function() { return resolveFn; }, 36 | 'fs-extra': { 37 | remove: function(path, callback) { 38 | return callback(null, true); 39 | }, 40 | symlink: function(from, to, type, _, callback) { 41 | expect(from).to.eql('../dist'); 42 | expect(to).to.eql('www'); 43 | expect(type).to.eql('dir'); 44 | return callback(null, true); 45 | } 46 | } 47 | }); 48 | 49 | return createProject(project)(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /addon/mixins/cordova-events.js: -------------------------------------------------------------------------------- 1 | /* jshint esnext:true */ 2 | 3 | import Ember from 'ember'; 4 | 5 | // include this mixin to define cordova event listeners with an onCordova object 6 | // 7 | // onCordova supports arrays, strings, and anonymous functions, e.g.: 8 | // 9 | // ``` 10 | // export default MyEmberObject.extend({ 11 | // onCordova: { 12 | // pause: ['pauseListening', 'disconnectPeripheral'], 13 | // resume: 'resumeListening', 14 | // volumeup: function() { console.log('a little bit louder now'); } 15 | // } 16 | // }); 17 | // ``` 18 | export default Ember.Mixin.create({ 19 | cordova: Ember.inject.service(), 20 | 21 | subscribeToCordovaEvents: Ember.on('init', function() { 22 | var cordova = this.get('cordova'), 23 | onCordova = this.get('onCordova'); 24 | 25 | Ember.keys(onCordova).forEach(function(key) { 26 | var func = Ember.get(onCordova, key); 27 | 28 | // subscribe to events 29 | if (func instanceof Array) { 30 | func.forEach(function(fn) { 31 | if (this._validateIsFunction(fn)) { 32 | cordova.on(key, this, fn); 33 | } 34 | }, this); 35 | } else { 36 | if (this._validateIsFunction(func)) { 37 | cordova.on(key, this, func); 38 | } 39 | } 40 | }, this); 41 | }), 42 | 43 | _validateIsFunction: function(func) { 44 | var isFunction = false; 45 | 46 | if (func instanceof Function) { 47 | isFunction = true; 48 | } else if (typeof func === 'string') { 49 | var fn = this.get(func); 50 | 51 | isFunction = fn instanceof Function; 52 | } 53 | 54 | return isFunction; 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "QUnit", 10 | "define", 11 | "console", 12 | "equal", 13 | "notEqual", 14 | "notStrictEqual", 15 | "test", 16 | "asyncTest", 17 | "testBoth", 18 | "testWithDefault", 19 | "raises", 20 | "throws", 21 | "deepEqual", 22 | "start", 23 | "stop", 24 | "ok", 25 | "strictEqual", 26 | "module", 27 | "moduleFor", 28 | "moduleForComponent", 29 | "moduleForModel", 30 | "process", 31 | "expect", 32 | "visit", 33 | "exists", 34 | "fillIn", 35 | "click", 36 | "keyEvent", 37 | "triggerEvent", 38 | "find", 39 | "findWithAssert", 40 | "wait", 41 | "DS", 42 | "keyEvent", 43 | "isolatedContainer", 44 | "startApp", 45 | "andThen", 46 | "currentURL", 47 | "currentPath", 48 | "currentRouteName", 49 | "cordova", 50 | "describe", 51 | "before", 52 | "after", 53 | "it", 54 | "lazy", 55 | "helper", 56 | "sinon" 57 | ], 58 | "node": false, 59 | "browser": false, 60 | "boss": true, 61 | "curly": false, 62 | "debug": false, 63 | "devel": false, 64 | "eqeqeq": true, 65 | "evil": true, 66 | "forin": false, 67 | "immed": false, 68 | "laxbreak": false, 69 | "newcap": true, 70 | "noarg": true, 71 | "noempty": false, 72 | "nonew": false, 73 | "nomen": false, 74 | "onevar": false, 75 | "plusplus": false, 76 | "regexp": false, 77 | "undef": true, 78 | "sub": true, 79 | "strict": false, 80 | "white": false, 81 | "eqnull": true, 82 | "esnext": true 83 | } 84 | -------------------------------------------------------------------------------- /docs/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | ## Description 4 | 5 | ember-cli-cordova creates a service at `services/cordova.js`, which listens for 6 | events defined and emitted by Cordova (e.g. `deviceready`, `pause`, `resume`). 7 | 8 | 9 | ## Usage 10 | 11 | **Simple Usage:** 12 | 13 | Many events require only simple callbacks, e.g. pausing / resuming listener 14 | functions, logging the event, etc. 15 | 16 | In these cases, extend your object/class with the provided mixin, which will 17 | look for an `onCordova` object and run the provided functions when an event 18 | matching the key is emitted. For example: 19 | 20 | ```javascript 21 | import CordovaEventsMixin from 'ember-cli-cordova/mixins/cordova-events'; 22 | 23 | export default MyEmberObject.extend( 24 | CordovaEventsMixin, { 25 | 26 | onCordova: { 27 | pause: ['pauseListening', 'disconnectPeripheral'], 28 | resume: 'resumeListening', 29 | volumeup: function() { console.log('a little bit louder now'); } 30 | } 31 | }); 32 | ``` 33 | 34 | (Yes, `onCordova` supports arrays of named functions, single named functions, 35 | and anonymous functions!) 36 | 37 | **Advanced Usage:** 38 | 39 | If you have more advanced needs, e.g. turning on/off an event subscription when 40 | an `Ember.Route` is activated/deactivated, or just prefer a more manual 41 | approach, you can inject the provided service and tinker away: 42 | 43 | ```javascript 44 | export default MyRoute.extend({ 45 | cordova: Ember.inject.service(), 46 | 47 | activate: function() { 48 | // use named function to unsubscribe later 49 | this.get('cordova').on('pause', this, '_resumeListening'); 50 | }, 51 | 52 | deactivate: function() { 53 | this.get('cordova').off('pause', this, '_resumeListening'); 54 | }, 55 | 56 | _resumeListening: function() { 57 | console.log('do your thing'); 58 | } 59 | }); 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | ##Overview 2 | 3 | All commands follow the pattern `ember cordova:{command}`. You can use the `cdv` alias 4 | insted of`cordova`, for example `ember cdv:{command}`. 5 | 6 | ##Open 7 | 8 | ####Description 9 | 10 | Open the native platform project with the default or specified application 11 | 12 | ####Available options 13 | + platform (default:ios) 14 | + application (default:system default application) 15 | 16 | ####Examples 17 | + `ember cordova:open` 18 | + `ember cordova:open --platform=android --application=eclipse` 19 | 20 | 21 | ##Archive 22 | 23 | ####Description 24 | 25 | Build project and create xcode archive. If the tag or commit options are present 26 | they will be performed after archiving. 27 | 28 | ####Available options 29 | + environment (default:staging) 30 | + tag (default:false) 31 | + commit (default:false) 32 | 33 | ####Examples 34 | + `ember cordova:archive` 35 | + `ember cordova:archive 0.0.2 --environment=staging --commit --tag ` 36 | 37 | ##Build 38 | 39 | ####Description 40 | 41 | Build the ember and cordova project together running in the simulator or on a device 42 | 43 | ####Available options 44 | + environment (default:development) 45 | + platform (default:ios) 46 | 47 | ####Examples 48 | + `ember cordova:build` 49 | + `ember cordova:build --environment=production --platform=ios` 50 | 51 | ##Prepare 52 | 53 | ####Description 54 | 55 | Needed after cloning or copying a project. 56 | 57 | ###Available options 58 | 59 | ####Examples 60 | + `ember cordova:prepare` 61 | 62 | ##Cordova 63 | 64 | ####Description 65 | 66 | Passes commands(plugin(s), platform(s), run, emulate) and arguments to the cordova command 67 | 68 | ###Available options 69 | + run 70 | + emulate 71 | + platform(s) 72 | + plugin(s) 73 | 74 | ####Examples 75 | + `ember cordova platform` 76 | + `ember cordova platforms` 77 | + `ember cordova run` 78 | -------------------------------------------------------------------------------- /lib/ext/promise.js: -------------------------------------------------------------------------------- 1 | // https://github.com/stefanpenner/ember-cli/blob/master/lib/ext/promise.js 2 | 'use strict'; 3 | 4 | var RSVP = require('rsvp'); 5 | var Promise = RSVP.Promise; 6 | 7 | module.exports = PromiseExt; 8 | 9 | // Utility functions on on the native CTOR need some massaging 10 | module.exports.hash = function() { 11 | return this.resolve(RSVP.hash.apply(null, arguments)); 12 | }; 13 | 14 | module.exports.denodeify = function() { 15 | var fn = RSVP.denodeify.apply(null, arguments); 16 | var Constructor = this; 17 | 18 | return function() { 19 | return Constructor.resolve(fn.apply(null, arguments)); 20 | }; 21 | }; 22 | 23 | module.exports.filter = function() { 24 | return this.resolve(RSVP.filter.apply(null, arguments)); 25 | }; 26 | 27 | module.exports.map = function() { 28 | return this.resolve(RSVP.map.apply(null, arguments)); 29 | }; 30 | 31 | function PromiseExt() { 32 | Promise.apply(this, arguments); 33 | } 34 | 35 | PromiseExt.prototype = Object.create(Promise.prototype); 36 | PromiseExt.prototype.constructor = PromiseExt; 37 | PromiseExt.__proto__ = Promise; 38 | 39 | PromiseExt.prototype.returns = function(value) { 40 | return this.then(function() { 41 | return value; 42 | }); 43 | }; 44 | 45 | PromiseExt.prototype.invoke = function(method) { 46 | var args = Array.prototype.slice(arguments, 1); 47 | 48 | return this.then(function(value) { 49 | return value[method].apply(value, args); 50 | }, undefined, 'invoke: ' + method + ' with: ' + args); 51 | }; 52 | 53 | PromiseExt.prototype.map = function(mapFn) { 54 | var Constructor = this.constructor; 55 | 56 | return this.then(function(values) { 57 | return Constructor.map(values, mapFn); 58 | }); 59 | }; 60 | 61 | PromiseExt.prototype.filter = function(mapFn) { 62 | var Constructor = this.constructor; 63 | 64 | return this.then(function(values) { 65 | return Constructor.filter(values, mapFn); 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /blueprints/cordova-starter-kit/files/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | var os = require('os'); 4 | var ifaces = os.networkInterfaces(); 5 | 6 | var addresses = []; 7 | for (var dev in ifaces) { 8 | ifaces[dev].forEach(function(details){ 9 | if(details.family === 'IPv4' && details.address !== '127.0.0.1') { 10 | addresses.push(details.address); 11 | } 12 | }); 13 | } 14 | 15 | module.exports = function(environment) { 16 | var ENV = { 17 | modulePrefix: '<%= modulePrefix %>', 18 | environment: environment, 19 | baseURL: '/', 20 | defaultLocationType: 'auto', 21 | EmberENV: { 22 | FEATURES: { 23 | // Here you can enable experimental features on an ember canary build 24 | // e.g. 'with-controller': true 25 | } 26 | }, 27 | 28 | APP: { 29 | // Here you can pass flags/options to your application instance 30 | // when it is created 31 | }, 32 | 33 | cordova: { 34 | rebuildOnChange: false, 35 | emulate: false, 36 | emberUrl: 'http://' + addresses[0] + ':4200', 37 | liveReload: { 38 | enabled: false, 39 | platform: 'ios' 40 | } 41 | } 42 | }; 43 | 44 | if (environment === 'development') { 45 | // ENV.APP.LOG_RESOLVER = true; 46 | ENV.APP.LOG_ACTIVE_GENERATION = true; 47 | // ENV.APP.LOG_TRANSITIONS = true; 48 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 49 | ENV.APP.LOG_VIEW_LOOKUPS = true; 50 | 51 | ENV.apiUrl = 'http://' + addresses[0] + ':3000/api/v1'; 52 | ENV.development = true; 53 | } 54 | 55 | if (environment === 'test') { 56 | // Testem prefers this... 57 | ENV.baseURL = '/'; 58 | ENV.locationType = 'auto'; 59 | 60 | // keep test console output quieter 61 | ENV.APP.LOG_ACTIVE_GENERATION = false; 62 | ENV.APP.LOG_VIEW_LOOKUPS = false; 63 | 64 | ENV.APP.rootElement = '#ember-testing'; 65 | } 66 | 67 | if (environment === 'staging') { 68 | ENV.apiUrl = 'http://<%= modulePrefix %>-staging.herokuapp.com/api/v1'; 69 | ENV.staging = true; 70 | } 71 | 72 | 73 | if (environment === 'production') { 74 | ENV.apiUrl = 'http://<%= modulePrefix %>.herokuapp.com/api/v1'; 75 | ENV.production = true; 76 | } 77 | 78 | return ENV; 79 | }; 80 | -------------------------------------------------------------------------------- /addon/mixins/routes/nav-bar.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Mixin.create({ 4 | _navController: Ember.computed('nav.controller', function() { 5 | var name = this.get('nav.controller') || 'application'; 6 | 7 | return this.controllerFor(name); 8 | }), 9 | 10 | afterModel: function(model) { 11 | this._setDefaults(); 12 | this._setNavOptions(model); 13 | this._setNavActions(); 14 | 15 | return this._super.apply(this, arguments); 16 | }, 17 | 18 | // Since we are using so many nested paths this makes sure they are set to 19 | // null values 20 | _setDefaults: function() { 21 | var ctrl = this.get('_navController'); 22 | 23 | if(!ctrl.get('nav')) { 24 | ctrl.send('resetNavBar'); 25 | 26 | } else if(!ctrl.get('nav.title')) { 27 | ctrl.set('nav.title', {}); 28 | 29 | } else if(!ctrl.get('nav.leftButton')) { 30 | ctrl.set('nav.leftButton', {}); 31 | 32 | } else if(!ctrl.get('nav.rightButton')) { 33 | ctrl.set('nav.rightButton', {}); 34 | } 35 | }, 36 | 37 | _setNavOptions: function(model) { 38 | var ctrl = this.get('_navController'); 39 | 40 | var navOptions = Ember.A([ 41 | 'title.text', 42 | 'leftButton.text', 'leftButton.icon', 43 | 'rightButton.text', 'rightButton.icon' 44 | ]); 45 | 46 | navOptions.forEach(function(key){ 47 | var optionPath = 'nav.' + key; 48 | var value = this.get(optionPath); 49 | 50 | if (value) { 51 | if(Ember.typeOf(value) === 'function') { 52 | value = value.call(this, model); 53 | } 54 | 55 | ctrl.set(optionPath, value); 56 | } 57 | }, this); 58 | }, 59 | 60 | _setNavActions: function() { 61 | var ctrl = this.get('_navController'); 62 | 63 | Ember.A(['leftButton', 'rightButton']).forEach(function(button) { 64 | var actionPath = 'nav.' + button + '.action'; 65 | 66 | var action = this.get(actionPath); 67 | if (action) { 68 | ctrl.set(actionPath, Ember.run.bind(this, action)); 69 | } 70 | }, this); 71 | }, 72 | 73 | actions: { 74 | willTransition: function() { 75 | this.get('_navController').send('resetNavBar'); 76 | return this._super.apply(this, arguments); 77 | } 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /lib/tasks/archive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var runCommand = require('../utils/run-command'); 5 | var Promise = require('../ext/promise'); 6 | var defaultPlatform = require('../utils/default-platform'); 7 | 8 | module.exports = function(version, options, project, platform) { 9 | var config = project.cordovaConfig; 10 | var updateXml = function() { return Promise.resolve(); }; 11 | var archiveProject = function() { return Promise.resolve(); }; 12 | 13 | platform = platform || 'ios'; 14 | 15 | if (version) { 16 | updateXml = require('./update-config-xml-version')(version, project); 17 | } 18 | 19 | var build = require('./build')(options.environment, platform, project); 20 | 21 | if(platform === 'ios') { 22 | var iosPath = path.join(project.root, 'cordova', 'platforms/ios'); 23 | var archiveCommand = 'xcodebuild -scheme ' + config.name + ' archive'; 24 | var archiveMessage = 'Archiving project with xcodebuild'; 25 | archiveProject = runCommand(archiveCommand, archiveMessage, { 26 | cwd: iosPath 27 | }); 28 | } 29 | 30 | if(platform === 'android' && version) { 31 | var __updateXml = updateXml; 32 | 33 | updateXml = function () { 34 | var androidVersionCode = require('./update-config-xml-android-version-code')(project); 35 | return __updateXml().then(androidVersionCode); 36 | } 37 | } 38 | 39 | var commitCommand = 'git add . && git commit -m "archive version: ' 40 | + version + '"'; 41 | var commitProject = runCommand(commitCommand, 'Commiting changes', { 42 | cwd: project.root 43 | }); 44 | 45 | var tagCommand = 'git tag -a -m "' + 'Version ' + version + '" ' + version; 46 | var tagProject = runCommand(tagCommand, 'Tagging with version ' + version, { 47 | cwd: project.root 48 | }); 49 | 50 | return function() { 51 | var promises = updateXml().then(build).then(archiveProject); 52 | 53 | if (options.commit && options.tag) { 54 | promises.then(commitProject).then(tagProject); 55 | } else { 56 | if (options.commit) { 57 | promises.then(commitProject) 58 | } else if (options.tag) { 59 | promises.then(tagProject); 60 | } 61 | } 62 | 63 | return promises; 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/open-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Open', function() { 2 | var project; 3 | beforeEach(function() { 4 | project = newProject(); 5 | }); 6 | 7 | it('rejects when the platform isn\'t supported', function() { 8 | var open = proxyquire('../../lib/tasks/open', {}); 9 | 10 | return open(project, 'fake-platform').catch(function(err) { 11 | expect(err.toString()).to.match(/platform is not supported/); 12 | }); 13 | }); 14 | 15 | describe('runs correct command on each platform', function() { 16 | var platform; 17 | before(function() { 18 | platform = process.platform; 19 | }); 20 | 21 | after(function() { 22 | process.platform = platform; 23 | }); 24 | 25 | describe('darwin', function() { 26 | beforeEach(function() { 27 | process.platform = 'darwin'; 28 | }); 29 | 30 | it('ios', function() { 31 | return assertOpenCommand(project, 'ios', 'open project-root/cordova/platforms/ios/*.xcodeproj'); 32 | }); 33 | 34 | it('android', function() { 35 | return assertOpenCommand(project, 'android', 'open project-root/cordova/platforms/android/.project'); 36 | }); 37 | }); 38 | 39 | describe('win32', function() { 40 | beforeEach(function() { 41 | process.platform = 'win32'; 42 | }); 43 | 44 | it('ios', function() { 45 | return assertOpenCommand(project, 'ios', 'start project-root/cordova/platforms/ios/*.xcodeproj'); 46 | }); 47 | 48 | it('android', function() { 49 | return assertOpenCommand(project, 'android', 'start project-root/cordova/platforms/android/.project'); 50 | }); 51 | }); 52 | 53 | describe('other', function() { 54 | beforeEach(function() { 55 | process.platform = 'other'; 56 | }); 57 | 58 | it('ios', function() { 59 | return assertOpenCommand(project, 'ios', 'xdg-open project-root/cordova/platforms/ios/*.xcodeproj'); 60 | }); 61 | 62 | it('android', function() { 63 | return assertOpenCommand(project, 'android', 'xdg-open project-root/cordova/platforms/android/.project'); 64 | }); 65 | }); 66 | }); 67 | }); 68 | 69 | function assertOpenCommand(project, platform, assertion) { 70 | var open = proxyquire('../../lib/tasks/open', { 71 | '../utils/run-command': function(command) { 72 | expect(command).to.eql(assertion); 73 | } 74 | }); 75 | return open(project, platform); 76 | } 77 | -------------------------------------------------------------------------------- /docs/nav-bar.md: -------------------------------------------------------------------------------- 1 | # Nav Bar 2 | 3 | ## Description 4 | 5 | The nav-bar component, partial, and mixins provide an easy to use way of having 6 | a global nav bar that gets updated between routes automatically by defining 7 | options on the route. 8 | 9 | The component itself is very barebones but there is also a partial you can 10 | include within it along with a few mixins to get a full featured nav bar without 11 | much work. 12 | 13 | # Usage 14 | 15 | Add this in your application template 16 | 17 | ```hbs 18 | {{#cdv-nav-bar}} 19 | {{partial 'cdv-generic-nav-bar'}} 20 | {{/cdv-nav-bar }} 21 | ``` 22 | 23 | In your application controller, mixin the controller NavBarMixin. If you don't 24 | do this, the actions within the nav-bar won't work. This is where all state for 25 | the nav bar lives. 26 | 27 | ```js 28 | import NavBarMixin from 'ember-cli-cordova/mixins/controllers/nav-bar'; 29 | 30 | export default Ember.Controller.extend(NavBarMixin); 31 | ``` 32 | 33 | Then in any route you can mixin the route NavBarMixin and set options for the 34 | nav bar. The options will be reset on each transition. This is implemented using 35 | ember's 36 | [willTransition](http://emberjs.com/api/classes/Ember.Route.html#event_willTransition) in the route. All options are optional. 37 | 38 | ```js 39 | import NavBarMixin from 'ember-cli-cordova/mixins/routes/nav-bar'; 40 | 41 | export default Ember.Route.extend(NavBarMixin, { 42 | nav: { 43 | // Default: application 44 | // If the cdv-nav-bar is included in something other than the application 45 | // template this needs to be set to that 46 | controller: 'application', 47 | 48 | 49 | // The text or icon option for title, leftButton, or rightButton can be 50 | // a string or function. If it's a function // it will be called in the 51 | // context of the afterModel hook and have the // model passed in as an 52 | // argument 53 | 54 | title: { 55 | text: 'Title' 56 | }, 57 | 58 | leftButton: { 59 | // Text to show 60 | text: function(model) { 61 | return model.get('title'); 62 | }, 63 | 64 | // Class of an icon to display 65 | icon: 'save', 66 | 67 | // Action to trigger when it is clicked. It will trigger in the context of 68 | // the route so you have access to the correct `this`. 69 | action: function() { 70 | // ... 71 | } 72 | }, 73 | 74 | // Same options as leftButton 75 | rightButton: { 76 | // ... 77 | }, 78 | } 79 | }); 80 | ``` 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-cordova", 3 | "version": "0.1.0-beta-2", 4 | "description": "A tool for creating hybrid apps using a combination of ember-cli and cordova ", 5 | "homepage": "https://github.com/poetic/ember-cli-cordova", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/poetic/ember-cli-cordova.git" 9 | }, 10 | "main": "index.js", 11 | "directories": { 12 | "doc": "docs", 13 | "test": "tests" 14 | }, 15 | "scripts": { 16 | "node-test": "mocha --require node-tests/helpers/_helper.js --reporter spec node-tests/**/*-test.js node-tests/**/**/*-test.js", 17 | "ember-test": "ember test", 18 | "test": "npm run node-test && npm run ember-test" 19 | }, 20 | "engines": { 21 | "node": ">= 0.10.0" 22 | }, 23 | "author": "Jake Craige ", 24 | "license": "MIT", 25 | "ember-addon": { 26 | "configPath": "tests/dummy/config" 27 | }, 28 | "keywords": [ 29 | "ember-addon" 30 | ], 31 | "dependencies": { 32 | "broccoli-funnel": "^0.2.3", 33 | "broccoli-merge-trees": "^1.1.0", 34 | "chalk": "^0.4.0", 35 | "ember-cli-babel": "^5.1.5", 36 | "findup-sync": "^0.1.3", 37 | "fs-extra": "^0.26.5", 38 | "lodash": "^4.5.1", 39 | "pleasant-progress": "^1.0.1", 40 | "recursive-readdir": "^1.1.1", 41 | "rsvp": "^3.0.6", 42 | "sinon": "^1.17.3", 43 | "underscore.string": "^2.3.3" 44 | }, 45 | "devDependencies": { 46 | "broccoli-asset-rev": "^2.2.0", 47 | "chai": "^1.9.1", 48 | "ember-ajax": "0.7.1", 49 | "ember-cli": "2.3.0", 50 | "ember-cli-app-version": "^1.0.0", 51 | "ember-cli-dependency-checker": "^1.2.0", 52 | "ember-cli-htmlbars": "^1.0.1", 53 | "ember-cli-htmlbars-inline-precompile": "^0.3.1", 54 | "ember-cli-inject-live-reload": "^1.3.1", 55 | "ember-cli-qunit": "^1.2.1", 56 | "ember-cli-release": "0.2.8", 57 | "ember-cli-sri": "^2.0.0", 58 | "ember-cli-uglify": "^1.2.0", 59 | "ember-disable-prototype-extensions": "^1.1.0", 60 | "ember-disable-proxy-controllers": "^1.0.1", 61 | "ember-export-application-global": "^1.0.4", 62 | "ember-load-initializers": "^0.5.0", 63 | "ember-resolver": "^2.0.3", 64 | "ember-sinon": "0.5.0", 65 | "ember-try": "^0.1.2", 66 | "glob": "^4.5.3", 67 | "loader.js": "^4.0.0", 68 | "mocha": "^1.20.1", 69 | "mocha-jshint": "0.0.7", 70 | "proxyquire": "^1.0.1", 71 | "qunit-bdd": "jakecraige/qunit-bdd.git#ember-addon", 72 | "tmp-sync": "^1.0.1" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /node-tests/unit/addon-test.js: -------------------------------------------------------------------------------- 1 | var addon = require('../../index'); 2 | 3 | function expectWithConfig(config, env, call) { 4 | if (call) { 5 | return expect(addon.config(env || 'development', config)); 6 | } 7 | else { 8 | return expect(addon.config.bind(addon, env || 'development', config)); 9 | } 10 | } 11 | 12 | var errRegex = /ember-cli-cordova: You must specify the locationType as 'hash' in your environment\.js/; 13 | 14 | describe('Addon', function () { 15 | describe('config', function () { 16 | var savedEnvVar; 17 | 18 | beforeEach(function () { 19 | savedEnvVar = process.env.EMBER_CLI_CORDOVA; 20 | }); 21 | 22 | afterEach(function () { 23 | process.env.EMBER_CLI_CORDOVA = savedEnvVar; 24 | }); 25 | 26 | describe('validates location type', function () { 27 | it('should throw Error when auto', function () { 28 | expectWithConfig({ 29 | locationType: 'auto' 30 | }).to.throw(Error, errRegex); 31 | }); 32 | 33 | it('should not throw an error when hash', function () { 34 | expectWithConfig({ 35 | locationType: 'hash' 36 | }).to.not.throw(Error, errRegex); 37 | }); 38 | 39 | it('should not throw an error with auto in test environment', function () { 40 | expectWithConfig({ 41 | locationType: 'auto' 42 | }, 'test').to.not.throw(Error, errRegex); 43 | }); 44 | 45 | it('should not throw an error when the env var is set to 0', function () { 46 | process.env.EMBER_CLI_CORDOVA = '0'; 47 | expectWithConfig({ 48 | locationType: 'auto' 49 | }).to.not.throw(Error, errRegex); 50 | }); 51 | }); 52 | 53 | describe('should replace the locationType', function () { 54 | it('should use the defaultLocationType when building for test', function () { 55 | expectWithConfig({ 56 | defaultLocationType: 'auto' 57 | }, 'test', true).to.have.property('locationType', 'auto'); 58 | }); 59 | 60 | it('should use the defaultLocationType when the env var is set to 0', function () { 61 | process.env.EMBER_CLI_CORDOVA = '0'; 62 | expectWithConfig({ 63 | defaultLocationType: 'auto' 64 | }, null, true).to.have.property('locationType', 'auto'); 65 | }); 66 | 67 | it('should use hash as locationType', function () { 68 | expectWithConfig({ 69 | defaultLocationType: 'auto' 70 | }, null, true).to.have.property('locationType', 'hash'); 71 | }); 72 | }); 73 | 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/build-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Build', function() { 2 | var project, commandOffset; 3 | beforeEach(function() { 4 | project = newProject(); 5 | commandOffset = 0; 6 | }); 7 | 8 | describe('env argument', function() { 9 | it('development - runs proper commands', function() { 10 | var commands = [ 11 | 'ember build --environment development', 12 | 'cordova build ios' 13 | ]; 14 | 15 | var build = proxyquire('../../lib/tasks/build', { 16 | '../utils/run-command': function(command) { 17 | expect(command).to.eql(commands[commandOffset++]); 18 | return resolveFn; 19 | }, 20 | '../tasks/link-environment': function() { 21 | return resolveFn; 22 | } 23 | }); 24 | 25 | return build('development', 'ios', project)(); 26 | }); 27 | 28 | it('production - runs proper commands', function() { 29 | var commands = [ 30 | 'ember build --environment production', 31 | 'cordova build ios --release' 32 | ]; 33 | 34 | var build = proxyquire('../../lib/tasks/build', { 35 | '../utils/run-command': function(command) { 36 | expect(command).to.eql(commands[commandOffset++]); 37 | return resolveFn; 38 | }, 39 | '../tasks/link-environment': function() { 40 | return resolveFn; 41 | } 42 | }); 43 | 44 | return build('production', 'ios', project)(); 45 | }); 46 | }); 47 | 48 | describe('platform argument', function() { 49 | it('development - runs proper commands', function() { 50 | var commands = [ 51 | 'ember build --environment development', 52 | 'cordova build android' 53 | ]; 54 | 55 | var build = proxyquire('../../lib/tasks/build', { 56 | '../utils/run-command': function(command) { 57 | expect(command).to.eql(commands[commandOffset++]); 58 | return resolveFn; 59 | }, 60 | '../tasks/link-environment': function() { 61 | return resolveFn; 62 | } 63 | }); 64 | 65 | return build('development', 'android', project)(); 66 | }); 67 | 68 | it('production - runs proper commands', function() { 69 | var commands = [ 70 | 'ember build --environment production', 71 | 'cordova build android --release' 72 | ]; 73 | 74 | var build = proxyquire('../../lib/tasks/build', { 75 | '../utils/run-command': function(command) { 76 | expect(command).to.eql(commands[commandOffset++]); 77 | return resolveFn; 78 | }, 79 | '../tasks/link-environment': function() { 80 | return resolveFn; 81 | } 82 | }); 83 | 84 | return build('production', 'android', project)(); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/post-build-test.js: -------------------------------------------------------------------------------- 1 | var uiMock = { write: noop }; 2 | 3 | describe('Tasks - Post Build', function() { 4 | var project; 5 | beforeEach(function() { 6 | project = newProject(); 7 | }); 8 | 9 | var options; 10 | describe('rebuildOnChange is false', function() { 11 | beforeEach(function() { 12 | options = { rebuildOnChange: false }; 13 | }); 14 | 15 | it('return noop', function() { 16 | var postBuild = proxyquire('../../lib/tasks/post-build', {}); 17 | 18 | var res = postBuild({}, options); 19 | 20 | expect(res.toString()).to.eql(noop.toString()); 21 | }); 22 | }); 23 | 24 | describe('rebuildOnChange is true', function() { 25 | beforeEach(function() { 26 | options = { rebuildOnChange: true }; 27 | }); 28 | 29 | it('returns resolving promise and executes correct build', function() { 30 | var postBuild = proxyquire('../../lib/tasks/post-build', { 31 | '../utils/run-command': function() { 32 | return resolveFn; 33 | }, 34 | '../ui': uiMock 35 | }); 36 | 37 | return postBuild(project, options)().then(function() { 38 | expect(true).to.be.ok; 39 | }); 40 | }); 41 | 42 | describe('emulate is false', function() { 43 | beforeEach(function() { 44 | options.emulate = false; 45 | }); 46 | 47 | it('runs correct command', function() { 48 | var postBuild = proxyquire('../../lib/tasks/post-build', { 49 | '../utils/run-command': function(command){ 50 | expect(command).to.eql('cordova build ios'); 51 | return resolveFn; 52 | }, 53 | '../ui': uiMock 54 | }); 55 | 56 | return postBuild(project, options)().then(function() { 57 | expect(true).to.be.ok; 58 | }); 59 | }); 60 | }); 61 | 62 | describe('emulate is true', function() { 63 | beforeEach(function() { 64 | options.emulate = true; 65 | }); 66 | 67 | it('runs correct command', function() { 68 | var postBuild = proxyquire('../../lib/tasks/post-build', { 69 | '../utils/run-command': function(command){ 70 | expect(command).to.eql('cordova build ios && cordova emulate ios'); 71 | return resolveFn; 72 | }, 73 | '../ui': uiMock 74 | }); 75 | 76 | return postBuild(project, options)().then(function() { 77 | expect(true).to.be.ok; 78 | }); 79 | }); 80 | }); 81 | 82 | describe('platform is android and emulate is true', function() { 83 | beforeEach(function() { 84 | options.platform = 'android'; 85 | options.emulate = true; 86 | }); 87 | 88 | it('runs correct command', function() { 89 | var postBuild = proxyquire('../../lib/tasks/post-build', { 90 | '../utils/run-command': function(command){ 91 | expect(command).to.eql('cordova build android && cordova emulate android'); 92 | return resolveFn; 93 | }, 94 | '../ui': uiMock 95 | }); 96 | 97 | return postBuild(project, options)().then(function() { 98 | expect(true).to.be.ok; 99 | }); 100 | }); 101 | }); 102 | }); 103 | }); 104 | 105 | 106 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var commands = require('./lib/commands'); 6 | var postBuild = require('./lib/tasks/post-build'); 7 | var defaults = require('lodash').defaults; 8 | var chalk = require('chalk'); 9 | var mergeTrees = require('broccoli-merge-trees'); 10 | var Funnel = require('broccoli-funnel'); 11 | 12 | module.exports = { 13 | name: 'ember-cli-cordova', 14 | 15 | _isTargetCordova: function () { 16 | return !process.env.EMBER_CLI_CORDOVA || 17 | ['0', 'off', 'false', 'no'].indexOf(process.env.EMBER_CLI_CORDOVA.toLowerCase()) === -1; 18 | }, 19 | 20 | config: function (env, config) { 21 | var conf = {isCordovaBuild: this._isTargetCordova()}; 22 | if (conf.isCordovaBuild && env !== 'test') { 23 | if (config.locationType && config.locationType !== 'hash') { 24 | throw new Error('ember-cli-cordova: You must specify the locationType as \'hash\' in your environment.js or rename it to defaultLocationType.'); 25 | } 26 | conf.locationType = 'hash'; 27 | } 28 | else if (!conf.locationType) { 29 | conf.locationType = config.defaultLocationType || 'auto'; 30 | } 31 | conf.cordova = defaults(config.cordova || {}, { 32 | liveReload: { 33 | enabled: false, 34 | platform: 'ios' 35 | } 36 | }); 37 | return conf; 38 | }, 39 | 40 | contentFor: function (type) { 41 | if (this._isTargetCordova() && type === 'body') { 42 | return ''; 43 | } 44 | }, 45 | 46 | includedCommands: function () { 47 | return commands; 48 | }, 49 | 50 | cdvConfig: function () { 51 | return this.project.config(process.env.EMBER_ENV || 'development').cordova; 52 | }, 53 | 54 | postBuild: function () { 55 | if (this._isTargetCordova()) { 56 | return postBuild(this.project, this.cdvConfig())(); 57 | } 58 | else { 59 | return function () { 60 | }; 61 | } 62 | }, 63 | 64 | treeForPublic: function (tree) { 65 | var config = this.cdvConfig(); 66 | 67 | if (this._isTargetCordova() && config.liveReload.enabled) { 68 | if (!config.liveReload.platform) { 69 | throw new Error('ember-cli-cordova: You must specify a liveReload.platform in your environment.js'); 70 | } 71 | 72 | var platformsPath = path.join(this.project.root, 'cordova', 'platforms'); 73 | var pluginsPath; 74 | 75 | if (config.liveReload.platform === 'ios') { 76 | pluginsPath = path.join(platformsPath, 'ios', 'www'); 77 | } 78 | else if (config.liveReload.platform === 'browser') { 79 | pluginsPath = path.join(platformsPath, 'browser', 'www'); 80 | } 81 | else if (config.liveReload.platform === 'android') { 82 | pluginsPath = path.join(platformsPath, 'android', 'assets', 'www'); 83 | } 84 | else { 85 | pluginsPath = path.join(platformsPath, config.liveReload.platform); 86 | } 87 | 88 | var files = ['cordova.js', 'cordova_plugins.js']; 89 | 90 | files.forEach(function (file) { 91 | var filePath = path.join(pluginsPath, file); 92 | if (!fs.existsSync(filePath)) { 93 | var err = new Error('ember-cli-cordova: ' + filePath + ' did not exist. It is required for Device LiveReload to work.'); 94 | err.stack = null; 95 | throw err; 96 | } 97 | }); 98 | 99 | if (fs.existsSync(path.join(pluginsPath, 'plugins'))) { 100 | files.push('plugins/**'); 101 | } 102 | 103 | var pluginsTree = new Funnel(this.treeGenerator(pluginsPath), { 104 | srcDir: '/', 105 | include: files, 106 | destDir: '/' 107 | }); 108 | 109 | console.log(chalk.green('ember-cli-cordova: Device LiveReload is enabled')); 110 | 111 | return mergeTrees([tree, pluginsTree]); 112 | } 113 | 114 | return tree; 115 | } 116 | }; 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-cordova [![Build Status](https://travis-ci.org/poetic/ember-cli-cordova.svg?branch=master)](https://travis-ci.org/poetic/ember-cli-cordova) [![Gitter](https://badges.gitter.im/poetic/ember-cli-cordova.svg)](https://gitter.im/poetic/ember-cli-cordova?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 2 | 3 | I will not be focusing on backward compatibility with older ember-cli versions 4 | as it's moving too fast and the API is constantly changing. I will always have 5 | this working with the latest stable release of ember-cli. 6 | 7 | ## Goals 8 | 9 | To provide a toolchain tightly integrated with ember-cli to make developing hybrid 10 | apps with cordova and ember as simple as possible. 11 | 12 | ## Supported Platforms 13 | 14 | Android and iOS. While we don't plan on actively supporting other platforms, 15 | feel free to open an issue or submit a pull request. 16 | 17 | ## Required Ember Versions 18 | 19 | Releases as of 0.1.0 require Ember 2.x and ember-cli 2.3.0. 20 | 21 | The lastest release for Ember 1.x is 0.0.19 and requires at least ember-cli >= 0.1.1 22 | 23 | ## Getting Started 24 | 25 | Please see our Getting Started guide 26 | [here](https://github.com/poetic/ember-cli-cordova/blob/master/docs/getting-started.md) 27 | 28 | ## Blueprints 29 | + `ember g cordova-init com.reverse.domain --platform=android` Required generator 30 | that sets up the cordova project with a few tweaks to the ember app 31 | + (optional) `ember g cordova-starter-kit` Adds some packages and files that makes up the 32 | base setup for projects I develop. 33 | 34 | ## Commands 35 | + `ember cordova:open` open xcode project 36 | + `ember cordova:build --environment=production --platform=ios` build cordova project 37 | + `ember cordova:archive 0.0.2 --environment=staging --commit --tag` archive ios project with xcode 38 | + `ember cordova:prepare` needs to be run after cloning a project 39 | + `ember cordova` Passes commands(plugin(s), platform(s), run, emulate) and arguments to the cordova command 40 | + `ember help` ember cli help with a section for addon provided commands as well 41 | 42 | # Docs 43 | 44 | Documentation can be found found in the docs directory [here](https://github.com/poetic/ember-cli-cordova/tree/master/docs). 45 | 46 | - [Getting Started](https://github.com/poetic/ember-cli-cordova/blob/master/docs/getting-started.md) 47 | - [Configuration](https://github.com/poetic/ember-cli-cordova/blob/master/docs/configuration.md) 48 | - [FAQ](https://github.com/poetic/ember-cli-cordova/blob/master/docs/faq.md) 49 | 50 | # ember-cordova 51 | 52 | [ember-cordova](https://github.com/isleofcode/ember-cordova) recently started as a fork of ember-cli-cordova by some 53 | contributors and maintainers. It only supports Ember 2, and includes 54 | added features such as build hooks, native splash screen & icon management, 55 | a platform service (e.g. isIOS) and an ember/cordova plugin ecosystem. 56 | 57 | ember-cli-cordova will still be maintained and active. 58 | 59 | It does not include features such as mobiletouch by default, starter 60 | blueprints and support for Ember <2. For these items, your best bet is 61 | to continue with ember-cli-cordova. 62 | 63 | # Dependency Docs 64 | 65 | - [ember-cli](http://ember-cli.com) 66 | - [cordova](http://cordova.apache.org/docs/en/4.0.0/) 67 | 68 | # Contributing 69 | 70 | ## Working with master 71 | 72 | ``` sh 73 | git clone https://github.com/poetic/ember-cli-cordova.git 74 | cd ember-cli-cordova 75 | npm i && bower i 76 | npm link 77 | ember new CordovaTest 78 | cd CordovaTest 79 | npm install --save-dev ember-cli-cordova 80 | npm link ember-cli-cordova 81 | ``` 82 | 83 | After this, any changes you make to the cloned repo will be instantly reflected 84 | in the test app you generated. It just symlinks the node_modules folder. 85 | 86 | # Example App 87 | 88 | You can find an example app using this here: 89 | [jakecraige/ember-cli-cordova-example-app](https://github.com/jakecraige/ember-cli-cordova-example-app) 90 | 91 | # Credits 92 | 93 | [ember-cli](https://github.com/stefanpenner/ember-cli) 94 | [ember](https://github.com/emberjs/emberjs) 95 | -------------------------------------------------------------------------------- /tests/integration/nav-bar-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import describeApp from '../helpers/describe-app'; 3 | 4 | describeApp('Integration - Nav Bar', function() { 5 | describe('Route Mixin', function() { 6 | describe('Template', function() { 7 | it('updates between routes', function() { 8 | visit('nav-bar'); 9 | 10 | andThen(function() { 11 | equal(find('header h1').text().trim(), 'Index'); 12 | equal(find('header button:first').text().trim(), 'iLeft'); 13 | equal(find('header button:last').text().trim(), 'iRight'); 14 | }); 15 | 16 | visit('nav-bar/page-1'); 17 | 18 | andThen(function() { 19 | equal(find('header h1').text().trim(), 'Page 1'); 20 | equal(find('header button:first').text().trim(), 'pLeft'); 21 | equal(find('header button:last').text().trim(), 'pRight'); 22 | }); 23 | }); 24 | 25 | it('values can be a function', function() { 26 | visit('nav-bar/options-from-model'); 27 | 28 | andThen(function() { 29 | var leftButton = find('header button:first'); 30 | var title = find('header h1'); 31 | var rightButton = find('header button:last'); 32 | 33 | equal(title.text().trim(), 'modelOption'); 34 | 35 | equal(leftButton.text().trim(), 'modelLeft'); 36 | ok(leftButton.find('i').hasClass('leftClass')); 37 | 38 | equal(rightButton.text().trim(), 'modelRight'); 39 | ok(rightButton.find('i').hasClass('rightClass')); 40 | }); 41 | }); 42 | }); 43 | 44 | describe('Actions', function() { 45 | before(function() { 46 | var navIndex = this.app.__container__.lookup('route:nav-bar/index'); 47 | var page1 = this.app.__container__.lookup('route:nav-bar/page-1'); 48 | var spec = this; 49 | 50 | spec.navIndexStub = sinon.stub(); 51 | navIndex.setProperties({ 52 | 'nav.leftButton.action': function() { 53 | spec.navIndexStub('left'); 54 | }, 55 | 'nav.rightButton.action': function() { 56 | spec.navIndexStub('right'); 57 | } 58 | }); 59 | 60 | spec.page1Stub = sinon.stub(); 61 | page1.setProperties({ 62 | 'nav.leftButton.action': function() { 63 | spec.page1Stub('left'); 64 | }, 65 | 'nav.rightButton.action': function() { 66 | spec.page1Stub('right'); 67 | } 68 | }); 69 | }); 70 | 71 | it('updates between routes', function() { 72 | visit('nav-bar'); 73 | 74 | click('header button:first'); 75 | 76 | andThen(Ember.run.bind(this, function() { 77 | ok(this.navIndexStub.calledWith('left')); 78 | ok(!this.navIndexStub.calledWith('right')); 79 | })); 80 | 81 | click('header button:last'); 82 | 83 | andThen(Ember.run.bind(this, function() { 84 | ok(this.navIndexStub.calledWith('left')); 85 | ok(this.navIndexStub.calledWith('right')); 86 | })); 87 | 88 | visit('nav-bar/page-1'); 89 | 90 | click('header button:first'); 91 | 92 | andThen(Ember.run.bind(this, function() { 93 | ok(this.page1Stub.calledWith('left')); 94 | ok(!this.page1Stub.calledWith('right')); 95 | })); 96 | 97 | click('header button:last'); 98 | 99 | andThen(Ember.run.bind(this, function() { 100 | ok(this.page1Stub.calledWith('left')); 101 | ok(this.page1Stub.calledWith('right')); 102 | })); 103 | }); 104 | }); 105 | 106 | describe('willTransiton reset', function() { 107 | it('resets nav options on transition', function() { 108 | visit('nav-bar'); 109 | 110 | andThen(function() { 111 | equal(find('header h1').text().trim(), 'Index'); 112 | }); 113 | 114 | visit('nav-bar/should-reset'); 115 | 116 | andThen(Ember.run.bind(this, function() { 117 | var ctrl = this.lookupController('nav-bar'); 118 | 119 | equal(find('header h1').text().trim(), ''); 120 | equal(find('header button:first').text().trim(), ''); 121 | equal(find('header button:last').text().trim(), ''); 122 | deepEqual(ctrl.get('nav'), {title: {}, leftButton: {}, rightButton: {}}); 123 | })); 124 | }); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /node-tests/unit/tasks/archive-test.js: -------------------------------------------------------------------------------- 1 | describe('Tasks - Archive', function() { 2 | var project; 3 | beforeEach(function() { 4 | project = newProject(); 5 | }); 6 | 7 | describe('version parameter validation', function() { 8 | it('calls config-xml-version with version', function() { 9 | var archiveVersion = '0.1.0'; 10 | 11 | var archive = proxyquire('../../lib/tasks/archive', { 12 | './update-config-xml-version': function(version, project) { 13 | expect(version).to.eql(archiveVersion); 14 | return resolveFn; 15 | }, 16 | './build': resolveFn, 17 | '../utils/run-command': resolveFn 18 | }); 19 | 20 | return archive(archiveVersion, {}, project)(); 21 | }); 22 | 23 | it('doesn\'t call config-xml-version with version when undefined', function() { 24 | var archive = proxyquire('../../lib/tasks/archive', { 25 | './update-config-xml-version': function(version, project) { 26 | expect(false, 'should not have called here').to.be.ok; 27 | return resolveFn; 28 | }, 29 | './build': resolveFn, 30 | '../utils/run-command': resolveFn 31 | }); 32 | 33 | return archive(undefined, {}, project)(); 34 | }); 35 | }); 36 | 37 | it('prepares proper commands', function() { 38 | var archiveVersion = "0.1.0"; 39 | var commandOffset = 0; 40 | 41 | var commands = [ 42 | 'xcodebuild -scheme ' + project.cordovaConfig.name + ' archive', 43 | 'git add . && git commit -m "archive version: ' + archiveVersion + '"', 44 | 'git tag -a -m "Version ' + archiveVersion + '" ' + archiveVersion 45 | ]; 46 | 47 | var archive = proxyquire('../../lib/tasks/archive', { 48 | '../utils/run-command': function(command, msg, options){ 49 | expect(command).to.eql(commands[commandOffset++]); 50 | return resolveFn; 51 | }, 52 | './update-config-xml-version': function(version, project) { 53 | expect(version).to.eql(archiveVersion) 54 | return resolveFn; 55 | }, 56 | './build': resolveFn 57 | }); 58 | 59 | return archive(archiveVersion, {}, project); 60 | }); 61 | 62 | describe('executes proper commands', function() { 63 | var archiveVersion; 64 | beforeEach(function() { 65 | archiveVersion = '0.1.0'; 66 | }); 67 | 68 | it('with no options', function() { 69 | var commands = [ 70 | 'update-config-xml-version', 71 | 'build', 72 | 'xcodebuild -scheme ' + project.cordovaConfig.name + ' archive' 73 | ]; 74 | 75 | var options = {}; 76 | 77 | return expectCommandsToBeCalled( 78 | archiveVersion, options, project, commands 79 | ); 80 | }); 81 | 82 | it('with commit option', function() { 83 | var commands = [ 84 | 'update-config-xml-version', 85 | 'build', 86 | 'xcodebuild -scheme ' + project.cordovaConfig.name + ' archive', 87 | 'git add . && git commit -m "archive version: ' + archiveVersion + '"' 88 | ]; 89 | 90 | var options = { commit: true }; 91 | 92 | return expectCommandsToBeCalled( 93 | archiveVersion, options, project, commands 94 | ); 95 | }); 96 | 97 | it('with tag option', function() { 98 | var commands = [ 99 | 'update-config-xml-version', 100 | 'build', 101 | 'xcodebuild -scheme ' + project.cordovaConfig.name + ' archive', 102 | 'git tag -a -m "Version ' + archiveVersion + '" ' + archiveVersion 103 | ]; 104 | 105 | var options = { tag: true }; 106 | 107 | return expectCommandsToBeCalled( 108 | archiveVersion, options, project, commands 109 | ); 110 | }); 111 | 112 | // TODO: It says tag is never called. But I see no reason why it shouldn't 113 | // be. Will need to investigate and fix later 114 | it.skip('with commit and tag option', function() { 115 | var commands = [ 116 | 'update-config-xml-version', 117 | 'build', 118 | 'xcodebuild -scheme ' + project.cordovaConfig.name + ' archive', 119 | 'git add . && git commit -m "archive version: ' + archiveVersion + '"', 120 | 'git tag -a -m "Version ' + archiveVersion + '" ' + archiveVersion 121 | ]; 122 | 123 | var options = { commit: true, tag: true }; 124 | 125 | return expectCommandsToBeCalled( 126 | archiveVersion, options, project, commands 127 | ); 128 | }); 129 | }); 130 | }); 131 | 132 | function expectCommandsToBeCalled(archiveVersion, options, project, commands) { 133 | var stubbedArchive = stubArchive(); 134 | var stubs = stubbedArchive.stubs; 135 | var archive = stubbedArchive.archive; 136 | 137 | return archive(archiveVersion, options, project)().then(function() { 138 | commands.forEach(function(command, index) { 139 | var stub = stubs[command]; 140 | if (stub) { 141 | expect(stub.called, command + ' was never called').to.be.ok; 142 | } else { 143 | expect(false, command + ' was never even stubbed').to.be.ok; 144 | } 145 | }); 146 | }); 147 | } 148 | 149 | function stubArchive() { 150 | var RSVP = require('rsvp'); 151 | var stubs = {}; 152 | var archive = proxyquire('../../lib/tasks/archive', { 153 | '../utils/run-command': function(command, msg, options){ 154 | return stubs[command] = sinon.stub().returns(RSVP.resolve(command)); 155 | }, 156 | 157 | './update-config-xml-version': function(version, project) { 158 | return stubs['update-config-xml-version'] = sinon.stub().returns( 159 | Promise.resolve('update-config-xml-version') 160 | ); 161 | }, 162 | 163 | './build': function(env, platform, project) { 164 | return stubs['build'] = sinon.stub().returns(Promise.resolve('build')); 165 | } 166 | }); 167 | 168 | return { 169 | stubs: stubs, 170 | archive: archive 171 | }; 172 | } 173 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This guide will walk you through setting up your first app with 4 | ember-cli-cordova. 5 | 6 | ## Prerequisites 7 | 8 | - [ember-cli](http://www.ember-cli.com) 9 | - [cordova](https://www.npmjs.org/package/cordova) 10 | 11 | Ember-cli-cordova requires ember-cli and cordova. You may install them as follows: 12 | 13 | ``` 14 | npm install -g ember-cli 15 | npm install -g cordova 16 | ``` 17 | 18 | ## Setting Up The App 19 | 20 | First, let's set up your ember-cli project: 21 | 22 | ```sh 23 | ember new hello 24 | ``` 25 | 26 | After that's set up, we need to add the ember-cli-cordova addon to the application. Go in the freshly generated `hello` folder, then install the addon. 27 | 28 | ```sh 29 | ember install ember-cli-cordova 30 | ``` 31 | 32 | Ember cli-cordova requires cordova. If you don't have cordova, use this line to install it. 33 | 34 | ```sh 35 | npm install -g cordova 36 | ``` 37 | 38 | To intialize the cordova project we use a generator provided by 39 | ember-cli-cordova. You pass in the com domain identifier that you want to use 40 | with your app. It can be anything you like as long as it's unique. This matters 41 | if you plan on releasing it to the app stores. It takes an optional `platform` 42 | argument that defaults to `ios`. If you want to generate an android project you 43 | would pass in `--platform=android` at the end or set your default platform in [cordova configuration](https://github.com/poetic/ember-cli-cordova/blob/master/docs/configuration.md). 44 | 45 | ```sh 46 | ember generate cordova-init com.poeticsystems.hello 47 | ``` 48 | 49 | This will prompt you to overwrite some default files. You should overwrite them 50 | as they provide some required settings and helpful additions. 51 | 52 | This will also create the cordova project for you in the `cordova` directory. If 53 | you ever need raw access to the cordova project you can `cd` into this directory 54 | to run the command or modify files. 55 | 56 | After that, the project is ready to go. There are some configuration options in 57 | your environment config that you can set to enable / disable some features. See 58 | the [configuration](https://github.com/poetic/ember-cli-cordova/blob/master/docs/configuration.md) section for information on that. 59 | 60 | ### Optional Starter Kit 61 | 62 | There's also a starter kit generator. This includes lots of goodies that we use 63 | at our company to develop our cordova apps. things like 64 | [broccoli-sass](https://github.com/joliss/broccoli-sass) for sass support and 65 | [liquid-fire](https://github.com/ef4/liquid-fire) for animations. It also 66 | includes a default application adapter, modal component, style resets and extra 67 | configuration. 68 | 69 | To run this, simple run 70 | 71 | ```sh 72 | ember generate cordova-starter-kit 73 | ``` 74 | 75 | ## Developing The App 76 | 77 | Once your project is set up, you're ready to start developing. We've tried to 78 | keep the experience as similar to ember-cli as possible. You just need to move 79 | your `locationType` setting in `config/environment.js` to `defaultLocationType` 80 | so that it'd be used when not building for cordova (cordova needs `hash` as 81 | `locationType`). 82 | 83 | Then you can simply run the `serve` command and begin 84 | 85 | ```sh 86 | ember serve 87 | ``` 88 | 89 | This will run the ember server and behave no different than in vanilla 90 | ember-cli. This is the primary place you will be working as it provides the 91 | quickest feedback loop, debugging tools and best experience. 92 | 93 | The one drawback to this is that any cordova plugins you use will not work in 94 | the browser. This means that when you want to test the functionality of 95 | a plugin, you will need to load the app up on a simulator or device. 96 | 97 | When you need to serve or build files for a browser environment (not for cordova) 98 | be sure to override the `EMBER_CLI_CORDOVA` environment variable to set it to a 99 | falsy value (`0`, `no`, `off` or `false`). It'll allow you to use `locationType` 100 | as specified in `defaultLocationType` and will not inject the `cordova.js` 101 | script tag. 102 | 103 | ```sh 104 | EMBER_CLI_CORDOVA=0 ember serve 105 | ``` 106 | 107 | ### Running The App On A Simulator Or Device 108 | 109 | When you need to run on a device or simulator, we have some options to automate 110 | this. By default, all features that affect the cordova build are disabled. You 111 | will need to enable the ones you want in the `config/environment.js`. 112 | 113 | #### Normal Cordova Build 114 | 115 | To run a simple cordova build with ember linked up, run 116 | 117 | ``` 118 | ember cordova:build 119 | ``` 120 | 121 | This will build your ember project, link everything up to cordova and run 122 | a cordova build. If you choose this route, no changes will be reflected in the 123 | running app until you run it again. To simplify, after every change you will 124 | need to run this command. Sometimes that's what you want, but we have an option 125 | to automate this. 126 | 127 | In your `config/environment` you can set `cordova.rebuildOnChange` to true. This 128 | will hook into the ember server and automate this build after every change. Then 129 | when you run the app again, you will be able to see the changes. 130 | 131 | While this is convenient at times(mainly plugin development) it's still not as 132 | quick as we want it to be. We want Livereload on the device! 133 | 134 | #### Livereload 135 | 136 | When enabled, this feature will allow you to use ember-cli's livereload when the 137 | app is running on you device or simulator. This allows you get instant feedback 138 | on a real device which allows you to get the full experience of using the app 139 | and plugins with an instant feedback loop. It is disabled in production...[for 140 | now](https://github.com/poetic/ember-cli-cordova/pull/56). 141 | 142 | Livereload is currently disabled by default and will need to be turned on in 143 | your `config/environment`. To enable it, set `cordova.liveReload.enabled` to 144 | true, and set `cordova.liveReload.platform` to the platform you will be running 145 | the app on. 146 | 147 | **A few things to be aware of** 148 | 149 | - You will need to rebuild with 'ember cordova:build' when you make changes to the 150 | environment config. 151 | - When you add/remove/update plugins or native code you will also need to run 152 | the `ember cordova:build`. 153 | - You will need to set the `emberUrl` in the config if you are running the app 154 | on a device that is not on the same computer or if your ember server is on 155 | a different port. It defaults to `http://localhost:4200`. The reason for this 156 | is that when the app starts up, it redirects to the url your ember server is 157 | running on so it must be set correctly. 158 | - Livereload is a fairly new feature in ember-cli-cordova and we are really 159 | excited about it. If you have any trouble with it please submit an issue or PR 160 | so that we can resolve it. 161 | --------------------------------------------------------------------------------