diff --git a/.gitignore b/.gitignore index 848cbb0b..8f01e812 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. +# See https://help.github.com/ignore-files/ for more about ignoring files. # compiled output /dist @@ -16,5 +16,5 @@ /connect.lock /coverage/* /libpeerconnection.log -npm-debug.log +npm-debug.log* testem.log diff --git a/.travis.yml b/.travis.yml index 54c17e00..30ff9030 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,45 @@ language: node_js node_js: -- '4' +- '6' notifications: email: false -sudo: false +dist: trusty +sudo: required + +addons: + apt: + sources: + - google-chrome + packages: + - google-chrome-stable + cache: directories: - - node_modules + - $HOME/.npm + - $HOME/.cache + before_install: - npm config set spin false - npm install -g bower - bower --version - npm install phantomjs-prebuilt - phantomjs --version + install: - npm install - bower install + +before_script: +- export DISPLAY=:99.0 +- sh -e /etc/init.d/xvfb start +- sleep 3 # give xvfb some time to start + script: - npm test + after_script: - scripts/travis-deploy.sh + env: global: - secure: Dwthqo6GaXQt7erj89xMa/2GRua9PTzcSKXYmxjE0O46DMuBR80JjPges0hGCWwPcdMFqsaMshSaTVF5eHc49vhaziB2sqWwLZndkRFIAsNGnBZkBpeZc9MsTNMbPP5KyJh/pG03nJLXUSz6xzMoxDNU/QHWyBYBxTlfxVGewLE= diff --git a/README.md b/README.md index 32b5d08f..a950e671 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [ember-twiddle](http://ember-twiddle.com) +# [ember-twiddle](https://ember-twiddle.com) [![Build Status][travis-badge]][travis-badge-url] [![Code Climate](https://codeclimate.com/github/ember-cli/ember-twiddle/badges/gpa.svg)](https://codeclimate.com/github/ember-cli/ember-twiddle) diff --git a/app/controllers/edit/revision.js b/app/controllers/edit/revision.js new file mode 100644 index 00000000..1a50fc70 --- /dev/null +++ b/app/controllers/edit/revision.js @@ -0,0 +1,4 @@ +import Ember from "ember"; +import GistController from "../mixins/gist-controller"; + +export default Ember.Controller.extend(GistController, {}); diff --git a/app/controllers/gist.js b/app/controllers/gist.js index 1f2d9d74..1a50fc70 100644 --- a/app/controllers/gist.js +++ b/app/controllers/gist.js @@ -1,53 +1,4 @@ import Ember from "ember"; +import GistController from "../mixins/gist-controller"; -const { inject, RSVP, run } = Ember; - -export default Ember.Controller.extend({ - fastboot: inject.service(), - - queryParams: ['numColumns', 'fullScreen', 'route', 'openFiles', 'fileTreeShown'], - numColumns: 1, - fullScreen: false, - openFiles: "", - fileTreeShown: true, - route: undefined, - applicationUrl: undefined, - unsaved: true, - - init(...args) { - this._super(...args); - if (!this.get('fastboot.isFastBoot')) { - this.setupWindowUpdate(); - } - }, - - actions: { - transitionQueryParams(queryParams) { - return this.transitionToRoute({ queryParams: queryParams }).then(() => { - return RSVP.resolve(queryParams); - }); - } - }, - - setupWindowUpdate: function() { - // TODO: this in a controller seems suspect, rather this should likely be - // part of some handshake, to ensure no races exist. This should likley not - // be something a controller would handle - (SP) - window.addEventListener('message', (m) => { - run(() => { - if(typeof m.data==='object' && 'setAppUrl' in m.data) { - if (!this.get('isDestroyed')) { - if (window.messagesWaiting > 0) { - window.messagesWaiting = 0; - } - const newRoute = m.data.setAppUrl || '/'; - this.setProperties({ - applicationUrl: newRoute, - route: newRoute === "/" ? undefined : newRoute - }); - } - } - }); - }); - } -}); +export default Ember.Controller.extend(GistController, {}); diff --git a/app/mixins/gist-controller.js b/app/mixins/gist-controller.js new file mode 100644 index 00000000..08ee7b38 --- /dev/null +++ b/app/mixins/gist-controller.js @@ -0,0 +1,53 @@ +import Ember from "ember"; + +const { inject, RSVP, run } = Ember; + +export default Ember.Mixin.create({ + fastboot: inject.service(), + + queryParams: ['numColumns', 'fullScreen', 'route', 'openFiles', 'fileTreeShown'], + numColumns: 1, + fullScreen: false, + openFiles: "", + fileTreeShown: true, + route: undefined, + applicationUrl: undefined, + unsaved: true, + + init(...args) { + this._super(...args); + if (!this.get('fastboot.isFastBoot')) { + this.setupWindowUpdate(); + } + }, + + actions: { + transitionQueryParams(queryParams) { + return this.transitionToRoute({ queryParams: queryParams }).then(() => { + return RSVP.resolve(queryParams); + }); + } + }, + + setupWindowUpdate: function() { + // TODO: this in a controller seems suspect, rather this should likely be + // part of some handshake, to ensure no races exist. This should likley not + // be something a controller would handle - (SP) + window.addEventListener('message', (m) => { + run(() => { + if(typeof m.data==='object' && 'setAppUrl' in m.data) { + if (!this.get('isDestroyed')) { + if (window.messagesWaiting > 0) { + window.messagesWaiting = 0; + } + const newRoute = m.data.setAppUrl || '/'; + this.setProperties({ + applicationUrl: newRoute, + route: newRoute === "/" ? undefined : newRoute + }); + } + } + }); + }); + } +}); diff --git a/app/models/settings.js b/app/models/settings.js index f6742624..67e3f1ba 100644 --- a/app/models/settings.js +++ b/app/models/settings.js @@ -3,8 +3,7 @@ import _merge from 'lodash/object/merge'; const { ObjectProxy, - get, - set + get } = Ember; export default ObjectProxy.extend({ @@ -18,8 +17,10 @@ export default ObjectProxy.extend({ const storageKey = get(this, 'storageKey'); const defaultSettings = get(this, 'defaultSettings'); const localSettings = get(this, 'isFastBoot') ? {} : JSON.parse(localStorage.getItem(storageKey)) || {}; + const newSettings = _merge(defaultSettings, localSettings); - set(this, 'content', _merge(defaultSettings, localSettings)); + this.content = Ember.Object.create(newSettings); + this.content.setProperties(this.content); this._super(...arguments); }, diff --git a/app/plugins/hbs-plugin.js b/app/plugins/hbs-plugin.js new file mode 100644 index 00000000..cf5ebee6 --- /dev/null +++ b/app/plugins/hbs-plugin.js @@ -0,0 +1,86 @@ +export default function(babel, _options) { + var options = _options || {}; + + function htmlbarsInlineCompilerPlugin(babel) { + var t = babel.types; + + var replaceNodeWithPrecompiledTemplate = function(node, template) { + var compiledTemplateString = "Ember.HTMLBars.compile(`" + template + "`)"; + + // Prefer calling replaceWithSourceString if it is present. + // this prevents a deprecation warning in Babel 5.6.7+. + // + // TODO: delete the fallback once we only support babel >= 5.6.7. + if (node.replaceWithSourceString) { + node.replaceWithSourceString(compiledTemplateString); + } else { + return compiledTemplateString; + } + }; + + + return new babel.Transformer('htmlbars-inline-precompile', { + ImportDeclaration: function(node, parent, scope, file) { + if (t.isLiteral(node.source, { value: "htmlbars-inline-precompile" })) { + var first = node.specifiers && node.specifiers[0]; + if (t.isImportDefaultSpecifier(first)) { + file.importSpecifier = first.local.name; + } else { + var input = file.code; + var usedImportStatement = input.slice(node.start, node.end); + var msg = "Only `import hbs from 'htmlbars-inline-precompile'` is supported. You used: `" + usedImportStatement + "`"; + throw file.errorWithNode(node, msg); + } + + // Prefer calling dangerouslyRemove instead of remove (if present) to + // suppress a deprecation warning. + // + // TODO: delete the fallback once we only support babel >= 5.5.0. + if (typeof this.dangerouslyRemove === 'function') { + this.dangerouslyRemove(); + } else { + this.remove(); + } + } + }, + + CallExpression: function(node, parent, scope, file) { + if (t.isIdentifier(node.callee, { name: file.importSpecifier })) { + var argumentErrorMsg = "hbs should be invoked with a single argument: the template string"; + if (node.arguments.length !== 1) { + throw file.errorWithNode(node, argumentErrorMsg); + } + + var template = node.arguments[0].value; + if (typeof template !== "string") { + throw file.errorWithNode(node, argumentErrorMsg); + } + + return replaceNodeWithPrecompiledTemplate(this, template); + } + }, + + TaggedTemplateExpression: function(node, parent, scope, file) { + if (t.isIdentifier(node.tag, { name: file.importSpecifier })) { + if (node.quasi.expressions.length) { + throw file.errorWithNode(node, "placeholders inside a tagged template string are not supported"); + } + + var template = node.quasi.quasis.map(function(quasi) { + return quasi.value.cooked; + }).join(""); + + return replaceNodeWithPrecompiledTemplate(this, template); + } + } + }); + } + + // used by broccoli-babel-transpiler to bust the cache when + // the template compiler being used changes + htmlbarsInlineCompilerPlugin.cacheKey = function() { + return options.cacheKey; + }; + + return htmlbarsInlineCompilerPlugin; +} diff --git a/app/routes/gist/edit/revision.js b/app/routes/gist/edit/revision.js index a8d061f4..ec9b1501 100644 --- a/app/routes/gist/edit/revision.js +++ b/app/routes/gist/edit/revision.js @@ -2,7 +2,6 @@ import GistEditRoute from "../edit"; export default GistEditRoute.extend({ - controllerName: 'gist', templateName: 'gist', model(params) { diff --git a/app/services/ember-cli.js b/app/services/ember-cli.js index bfa12768..e287c37c 100644 --- a/app/services/ember-cli.js +++ b/app/services/ember-cli.js @@ -1,17 +1,16 @@ import Babel from "npm:babel-core"; import Path from 'npm:path'; -import HtmlbarsInlinePrecompile from 'npm:babel-plugin-htmlbars-inline-precompile'; +import HbsPlugin from '../plugins/hbs-plugin'; import blueprints from '../lib/blueprints'; import config from '../config/environment'; import Ember from 'ember'; import moment from 'moment'; import _template from "lodash/string/template"; -const hbsPlugin = new HtmlbarsInlinePrecompile(Ember.HTMLBars.precompile); - const { computed, inject, RSVP, $ } = Ember; const twiddleAppName = 'twiddle'; const oldTwiddleAppNames = ['demo-app', 'app']; +const hbsPlugin = new HbsPlugin(Babel); // These files will be included if not present const boilerPlateJs = [ diff --git a/blueprints/twiddle.json b/blueprints/twiddle.json index 31717102..7f197ed7 100644 --- a/blueprints/twiddle.json +++ b/blueprints/twiddle.json @@ -1,5 +1,5 @@ { - "version": "0.10.7", + "version": "0.11.0", "EmberENV": { "FEATURES": {} }, diff --git a/bower.json b/bower.json index 5ef57694..915d5b81 100644 --- a/bower.json +++ b/bower.json @@ -2,7 +2,7 @@ "name": "ember-twiddle", "version": "0.10.7", "dependencies": { - "ember": "2.8.0", + "ember": "~2.10.2", "ember-cli-shims": "0.1.3", "jquery": "1.11.3", "codemirror": "~5.19.0", @@ -16,7 +16,7 @@ }, "resolutions": { "codemirror": "~5.19.0", - "ember": "2.8.0", + "ember": "~2.10.2", "FakeXMLHttpRequest": "^1.3.0", "route-recognizer": "^0.2.3" } diff --git a/config/environment.js b/config/environment.js index 038831ca..febbee85 100644 --- a/config/environment.js +++ b/config/environment.js @@ -24,6 +24,10 @@ module.exports = function(environment) { FEATURES: { // Here you can enable experimental features on an ember canary build // e.g. 'with-controller': true + }, + EXTEND_PROTOTYPES: { + // Prevent Ember Data from overriding Date.parse + Date: false } }, diff --git a/package.json b/package.json index 79b86508..29673110 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ember-twiddle", - "version": "0.10.7", + "version": "0.11.0", "description": "https://ember-twiddle.com", "private": true, "directories": { @@ -14,13 +14,13 @@ }, "repository": "https://github.com/ember-cli/ember-twiddle", "engines": { - "node": ">= 0.10.0" + "node": ">= 0.12.0" }, - "author": "", + "author": "Ember Twiddle Contributors", "license": "MIT", "devDependencies": { "babel": "^5.8.38", - "broccoli-asset-rev": "^2.4.2", + "broccoli-asset-rev": "^2.4.5", "broccoli-babel-transpiler": "~5.6.1", "broccoli-concat": "^2.3.2", "broccoli-funnel": "^1.0.1", @@ -32,9 +32,9 @@ "ember-api-actions": "~0.1.4", "ember-autoresize": "~0.5.17", "ember-browserify": "~1.1.13", - "ember-cli": "2.8.0", - "ember-cli-app-version": "~1.0.0", - "ember-cli-babel": "^5.1.6", + "ember-cli": "2.10.0", + "ember-cli-app-version": "^2.0.0", + "ember-cli-babel": "^5.1.7", "ember-cli-bootstrap-sassy": "~0.5.5", "ember-cli-build-notifications": "0.2.0", "ember-cli-code-coverage": "~0.3.4", @@ -45,22 +45,22 @@ "ember-cli-document-title": "~0.3.3", "ember-cli-fastboot": "1.0.0-beta.11", "ember-cli-file-creator": "~0.4.0", - "ember-cli-htmlbars": "~1.0.3", - "ember-cli-htmlbars-inline-precompile": "^0.3.1", - "ember-cli-inject-live-reload": "^1.4.0", - "ember-cli-jshint": "~1.0.0", + "ember-cli-htmlbars": "^1.0.10", + "ember-cli-htmlbars-inline-precompile": "^0.3.3", + "ember-cli-inject-live-reload": "^1.4.1", + "ember-cli-jshint": "^2.0.1", "ember-cli-jstree": "~1.0.7", "ember-cli-legacy-blueprints": "^0.1.0", "ember-cli-mirage": "0.2.3", "ember-cli-moment-shim": "~2.2.1", - "ember-cli-qunit": "~2.1.0", + "ember-cli-qunit": "^2.1.0", "ember-cli-release": "1.0.0-beta.2", "ember-cli-sass": "~6.0.0", "ember-cli-sri": "~2.1.0", "ember-cli-test-loader": "~1.1.0", "ember-cli-uglify": "~1.2.0", "ember-concurrency": "~0.7.1", - "ember-data": "2.8.0", + "ember-data": "~2.10.0", "ember-disable-proxy-controllers": "1.0.1", "ember-export-application-global": "~1.1.1", "ember-git-version": "~0.1.1", @@ -73,13 +73,12 @@ "ember-responsive": "~1.2.7", "ember-route-action-helper": "^1.0.0", "ivy-codemirror": "2.0.2", - "loader.js": "~4.0.1", + "loader.js": "~4.0.10", "moment": "~2.15.2", "moment-timezone": "~0.5.4", "torii": "0.8.0" }, "dependencies": { - "babel-core": "^5.8.38", - "babel-plugin-htmlbars-inline-precompile": "0.0.5" + "babel-core": "^5.8.38" } } diff --git a/testem.js b/testem.js index 26044b2f..4673ebf8 100644 --- a/testem.js +++ b/testem.js @@ -4,7 +4,7 @@ module.exports = { "test_page": "tests/index.html?hidepassed", "disable_watching": true, "launch_in_ci": [ - "PhantomJS" + "Chrome" ], "launch_in_dev": [ "PhantomJS", diff --git a/tests/helpers/destroy-app.js b/tests/helpers/destroy-app.js index 3a0114aa..0b779a7f 100644 --- a/tests/helpers/destroy-app.js +++ b/tests/helpers/destroy-app.js @@ -1,6 +1,8 @@ import Ember from 'ember'; export default function destroyApp(application) { - Ember.run(application, 'destroy'); - server.shutdown(); + Ember.run(() => { + application.destroy(); + window.server.shutdown(); + }); }