diff --git a/package.json b/package.json index cec164e029..b9fceb45b7 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "canonical-path": "0.0.2", "connect": "^3.3.4", "del": "~1.1.1", + "es6-module-loader": "^0.11.2", "gulp": "~3.8.10", "gulp-concat": "~2.5.0", "gulp-debug": "~2.0.1", @@ -25,8 +26,10 @@ "q": "^1.2.0", "request": "^2.53.0", "serve-static": "~1.8.1", + "systemjs": "^0.11.3", "systemjs-builder": "^0.9.1", "through2": "~0.6.3", + "traceur-runtime": "0.0.59", "yargs": "^3.6.0" }, "dependencies": { diff --git a/scripts/test/karma.conf.js b/scripts/test/karma.conf.js index fbbc5fdf75..34978f8ceb 100644 --- a/scripts/test/karma.conf.js +++ b/scripts/test/karma.conf.js @@ -8,12 +8,16 @@ module.exports = function(config) { frameworks: ['jasmine'], files: [ - // Sources and specs. - // Loaded through the es6-module-loader, in `test-main.js`. - {pattern: 'dist/ionic/**/*.js', included: false}, - ] - .concat(buildConfig.lib) - .concat('scripts/test/test-main.js'), + 'node_modules/systemjs/dist/system.js', + 'node_modules/es6-module-loader/dist/es6-module-loader.js', + 'node_modules/traceur-runtime/index.js', + 'node_modules/zone.js/zone.js', + 'node_modules/zone.js/long-stack-trace-zone.js', + 'dist/lib/angular2.js', + 'jspm-config.js', + 'scripts/test/test-main.js', + {pattern: 'src/**/*.spec.js', included: false}, + ], exclude: [ 'src/**/examples/**' diff --git a/scripts/test/test-main.js b/scripts/test/test-main.js index 71b002914e..0561be081e 100644 --- a/scripts/test/test-main.js +++ b/scripts/test/test-main.js @@ -8,7 +8,7 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 50; // we will call `__karma__.start()` later, once all the specs are loaded. __karma__.loaded = function() {}; -System.baseURL = 'http://localhost:9876/base/dist/'; +System.baseURL = 'http://localhost:9876/base/'; // So that we can import packages like `core/foo`, instead of `core/src/foo`. // System.paths = { @@ -37,7 +37,7 @@ Promise.all( }); function onlySpecFiles(path) { - return /_spec\.js$/.test(path); + return /\.spec\.js$/.test(path); } function file2moduleName(filePath) { return filePath.replace(/\\/g, '/') diff --git a/src/components/aside/aside.js b/src/components/aside/aside.js index ff92136afe..86096e6672 100644 --- a/src/components/aside/aside.js +++ b/src/components/aside/aside.js @@ -3,19 +3,6 @@ import {ComponentConfig} from '../../core/config/config' import * as types from './extensions/types/types' import * as gestures from './extensions/gestures/gestures'; -export let AsideConfig = new ComponentConfig() - -AsideConfig.property('side') - .when('left', gestures.LeftAsideGesture) - .when('right', gestures.RightAsideGesture) - .when('top', gestures.TopAsideGesture) - .when('bottom', gestures.BottomAsideGesture) - -AsideConfig.property('type') - .when('overlay', types.AsideTypeOverlay) - .when('push', types.AsideTypePush) - .when('reveal', types.AsideTypeReveal) - @Component({ selector: 'ion-aside', bind: { @@ -31,36 +18,35 @@ AsideConfig.property('type') export class Aside { constructor( @NgElement() element: NgElement, - @PropertySetter('style.transform') transformSetter: Function, - /* propertSetter doesn't work for classes right now */ - config: AsideConfig + configFactory: AsideConfig ) { this.domElement = element.domElement - // TODO inject constant instead of using domElement.getAttribute // TODO let config / platform handle defaults transparently - let side = this.side = this.domElement.getAttribute('side') || 'left' - let type = this.type = this.domElement.getAttribute('type') || 'overlay' - this.delegates = config.invoke(this, { side, type }) + this.side = this.domElement.getAttribute('side') || 'left' + this.type = this.domElement.getAttribute('type') || 'overlay' + + this.config = configFactory.create(this); + this.gestureDelegate = this.config.getDelegate('gesture'); + this.typeDelegate = this.config.getDelegate('type'); - this.domElement.classList.add(side) this.domElement.addEventListener('transitionend', ev => { this.setChanging(false) }) } setTransform(transform) { - this.delegates.type.setTransform(transform) + this.typeDelegate.setTransform(transform) } setSliding(isSliding) { if (isSliding !== this.isSliding) { - this.delegates.type.setSliding(isSliding) + this.typeDelegate.setSliding(isSliding) } } setChanging(isChanging) { if (isChanging !== this.isChanging) { this.isChanging = isChanging - this.domElement.classList[isChanging ? 'add' : 'remove']('changing') + this.domElement.classList[isChanging ? 'add' : 'remove']('changing'); } } setOpen(isOpen) { @@ -68,8 +54,24 @@ export class Aside { this.isOpen = isOpen this.setChanging(true) requestAnimationFrame(() => { - this.delegates.type.setOpen(isOpen) + this.typeDelegate.setOpen(isOpen) }) } } } + +export let AsideConfig = new ComponentConfig(Aside) + +AsideConfig.classes('side', 'type') + +AsideConfig.delegate('gesture') + .when({side: 'left'}, gestures.LeftAsideGesture) + .when({side: 'right'}, gestures.RightAsideGesture) + .when({side: 'top'}, gestures.TopAsideGesture) + .when({side: 'bottom'}, gestures.BottomAsideGesture) + +AsideConfig.delegate('type') + .when({type: 'overlay'}, types.AsideTypeOverlay) + .when({type: 'push'}, types.AsideTypePush) + .when({type: 'reveal'}, types.AsideTypeReveal) + diff --git a/src/components/aside/aside.scss b/src/components/aside/aside.scss index 1e3a12ad45..c111f414b9 100644 --- a/src/components/aside/aside.scss +++ b/src/components/aside/aside.scss @@ -2,7 +2,7 @@ $aside-width: 304px; $aside-height: 304px; $aside-transition: 0.3s linear transform; -ion-aside { +.aside { display: block; position: absolute; @@ -17,7 +17,7 @@ ion-aside { display: none; } - &.left { + &.aside-left { width: $aside-width; left: -$aside-width; top: 0; @@ -25,11 +25,11 @@ ion-aside { transform: translate3d(0, 0, 0); &.open, - &.type-reveal { + &.aside-reveal { transform: translate3d($aside-width,0,0); } } - &.right { + &.aside-right { width: $aside-width; left: 100%; top: 0; @@ -37,11 +37,11 @@ ion-aside { transform: translate3d(0,0,0); &.open, - &.type-reveal { + &.aside-reveal { transform: translate3d(-$aside-width,0,0); } } - &.top { + &.aside-top { height: $aside-width; top: -$aside-width; left: 0; @@ -49,11 +49,11 @@ ion-aside { transform: translate3d(0,0,0); &.open, - &.type-reveal { + &.aside-reveal { transform: translate3d(0,$aside-width,0); } } - &.bottom { + &.aside-bottom { height: $aside-width; top: 100%; left: 0; @@ -78,13 +78,3 @@ ion-aside { transform: translate3d(-$aside-width,0,0); } } - -ion-aside-parent { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: block; - overflow:hidden; -} diff --git a/src/components/aside/extensions/gestures/gestures.js b/src/components/aside/extensions/gestures/gestures.js index 7bcdedd11a..ac50604691 100644 --- a/src/components/aside/extensions/gestures/gestures.js +++ b/src/components/aside/extensions/gestures/gestures.js @@ -1,4 +1,5 @@ import {Aside} from '../../aside'; +//TODO: figure out way to get rid of all the ../../../../ import {SlideEdgeGesture} from '../../../../core/gestures/slide-edge-gesture'; class AsideGesture extends SlideEdgeGesture { diff --git a/src/components/aside/extensions/types/types.js b/src/components/aside/extensions/types/types.js index 8ef070260b..f59786ef6e 100644 --- a/src/components/aside/extensions/types/types.js +++ b/src/components/aside/extensions/types/types.js @@ -26,7 +26,7 @@ let contentManipulator = { } } -class AsideType { +export class AsideType { constructor(aside) { this.aside = aside; diff --git a/src/core/config/config.js b/src/core/config/config.js index c0cff5fdf8..90b8365a4b 100644 --- a/src/core/config/config.js +++ b/src/core/config/config.js @@ -1,42 +1,71 @@ -import {platform} from '../platform/platform'; -// import {ConfigCase} from './config-case'; -import * as util from '../../util'; +import {platform} from '../platform/platform' +import * as util from '../../util' /* -let MyConfig = new ComponentConfig(); -MyConfig.property('side') - .when('left', LeftSlideGesture) - .when('right', RightSlideGesture) - .when('top', TopSlideGesture) - .when('bottom', BottomSlideGesture) +let MyConfig = new ComponentConfig(MyComponent) +MyConfig.classes('classes') +MyConfig.delegate('gesture') + .when({side: 'left'}, LeftAsideGesture) + .when({side: 'right'}, RightAsideGesture) + .when({side: 'top'}, TopAsideGesture) + .when({side: 'bottom'}, BottomAsideGesture) */ -export function ComponentConfig() { +export function ComponentConfig(ComponentConstructor) { + let componentCssName = util.pascalCaseToDashCase(ComponentConstructor.name) + return class Config { - static property(propertyName) { - let self; - return (self = { - when(propertyValue, Class) { - Config.addCase(propertyName, propertyValue, Class); - return self; - } - }); + static classes() { + Config.classProperties || (Config.classProperties = []) + Config.classProperties.push.apply(Config.classProperties, arguments) } - static addCase(property, value, Class) { - Config.registry || (Config.registry = {}); - (Config.registry[property] || (Config.registry[property] = {}))[value] = Class; - } - invoke(instance, properties = {}) { - let delegates = {}; - for (let property in properties) { - let value = properties[property]; - let propertyRegistry = Config.registry && Config.registry[property] || {}; - if (propertyRegistry[value]) { - delegates[property] = new propertyRegistry[value](instance); + static delegate(delegateName) { + let self = { + when(condition, DelegateConstructor) { + Config.addCase(delegateName, condition, DelegateConstructor) + return self } } - return delegates; + return self } + static addCase(delegateName, condition, DelegateConstructor) { + Config.registry || (Config.registry = {}) + var array = (Config.registry[delegateName] || (Config.registry[delegateName] = [])) + + let callback = condition + if (util.isObject(callback)) { + // Support eg `{side: 'left'}` as a condition + callback = (instance) => { + for (let key in condition) { + if (condition.hasOwnProperty(key) && instance[key] !== condition[key]) { + return false + } + } + return true + } + } + array.unshift({ callback, DelegateConstructor }) + } + + create(instance) { + instance.domElement.classList.add(componentCssName) + for (let i = 0; i < (Config.classProperties || []).length; i++) { + let propertyValue = instance[Config.classProperties[i]] + instance.domElement.classList.add(`${componentCssName}-${propertyValue}`) + } + return { + getDelegate(delegateName) { + let registry = Config.registry && Config.registry[delegateName] || [] + for (let i = 0; i < registry.length; i++) { + let condition = registry[i] + if (condition.callback(instance)) { + return new condition.DelegateConstructor(instance) + } + } + } + } + } + } } @@ -65,84 +94,84 @@ class AsideReveal { // export class Config extends ConfigCase { // constructor() { -// this._root = this; -// this._cases = {}; +// this._root = this +// this._cases = {} // super({ // root: this, // parent: null, // path: '' -// }); +// }) // } // invoke(instance) { -// return invokeConfig(this, instance); +// return invokeConfig(this, instance) // } // _addCase(key, baseCase) { -// let path = baseCase._path.slice(); -// path.push(key); +// let path = baseCase._path.slice() +// path.push(key) // // Remove empties & duplicates // path = path // .filter((value, index) => { -// return value && path.indexOf(value) === index; +// return value && path.indexOf(value) === index // }) -// .sort(); +// .sort() // if (path.join(' ') === baseCase._path.join(' ')) { -// return baseCase; +// return baseCase // } -// return this._createCase(path); +// return this._createCase(path) // } // _createCase(path) { -// if (!path.length) return this; -// let pathStr = path.join(' '); -// let configCase = this._cases[pathStr]; +// if (!path.length) return this +// let pathStr = path.join(' ') +// let configCase = this._cases[pathStr] // if (!configCase) { -// let parentPath = path.slice(0, path.length - 1); +// let parentPath = path.slice(0, path.length - 1) // configCase = this._cases[pathStr] = new ConfigCase({ // root: this, // parent: this._createCase(parentPath), // path: path -// }); +// }) // } -// return configCase; +// return configCase // } // } // export function invokeConfig(config, object) { -// let platformName = platform.get().name; +// let platformName = platform.get().name // let passedCases = [config].concat( // Object.keys(config._cases) // .map(name => config._cases[name]) // .filter(configCasePasses) // .sort(function(a,b) { -// return a._path.length < b._path.length ? -1 : 1; +// return a._path.length < b._path.length ? -1 : 1 // }) -// ); +// ) // // Extend the given object with the values of all the passed cases, starting with the // // most specific. -// let defaults = [object]; +// let defaults = [object] // // Also find the most specific case with a component that we should use. -// let ComponentToUse; -// for (let i = 0, ii = passedCases.length; i < ii; i++) { -// defaults.push(passedCases[i]._values); +// let ComponentToUse +// for (let i = 0, ii = passedCases.length i < ii i++) { +// defaults.push(passedCases[i]._values) // if (passedCases[i]._component) { -// ComponentToUse = passedCases[i]._component; +// ComponentToUse = passedCases[i]._component // } // } -// util.defaults.apply(null, defaults); +// util.defaults.apply(null, defaults) -// return ComponentToUse; +// return ComponentToUse // function configCasePasses(configCase) { -// let path = configCase._path; -// let key; -// for (let i = 0, ii = path.length; i < ii; i++) { -// if (platformName !== path[i]) return false; +// let path = configCase._path +// let key +// for (let i = 0, ii = path.length i < ii i++) { +// if (platformName !== path[i]) return false // } -// return true; +// return true // } // } diff --git a/src/util.js b/src/util.js index cb85ac9d2d..596ac8c34c 100644 --- a/src/util.js +++ b/src/util.js @@ -28,19 +28,18 @@ export function defaults(dest) { return dest; } -export function isString(val) { - return typeof val === 'string'; +export let isString = val => typeof val === 'string'; +export let isFunction = val => typeof val === 'function'; +export let isDefined = val => typeof val === 'undefined'; +export let isObject = val => typeof val === 'object'; + +export function pascalCaseToDashCase(str = '') { + return str.charAt(0).toLowerCase() + str.substring(1).replace(/[A-Z]/g, function(match) { + return '-' + match.toLowerCase() + }) } -export function isFunction(val) { - return typeof val === 'function'; -} - -export function isDefined(val) { - return typeof val !== 'undefined'; -} - -export var array = { +export let array = { unique(array) { return array.filter(function(value, index) { return array.indexOf(value) === index;