repackaging: all the file moves
This commit is contained in:
319
modules/@angular/upgrade/src/upgrade_ng1_adapter.ts
Normal file
319
modules/@angular/upgrade/src/upgrade_ng1_adapter.ts
Normal file
@ -0,0 +1,319 @@
|
||||
import {
|
||||
Directive,
|
||||
DoCheck,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
OnInit,
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
Type
|
||||
} from 'angular2/core';
|
||||
import {
|
||||
NG1_COMPILE,
|
||||
NG1_SCOPE,
|
||||
NG1_HTTP_BACKEND,
|
||||
NG1_TEMPLATE_CACHE,
|
||||
NG1_CONTROLLER
|
||||
} from './constants';
|
||||
import {controllerKey} from './util';
|
||||
import * as angular from './angular_js';
|
||||
|
||||
const CAMEL_CASE = /([A-Z])/g;
|
||||
const INITIAL_VALUE = {
|
||||
__UNINITIALIZED__: true
|
||||
};
|
||||
const NOT_SUPPORTED: any = 'NOT_SUPPORTED';
|
||||
|
||||
|
||||
export class UpgradeNg1ComponentAdapterBuilder {
|
||||
type: Type;
|
||||
inputs: string[] = [];
|
||||
inputsRename: string[] = [];
|
||||
outputs: string[] = [];
|
||||
outputsRename: string[] = [];
|
||||
propertyOutputs: string[] = [];
|
||||
checkProperties: string[] = [];
|
||||
propertyMap: {[name: string]: string} = {};
|
||||
linkFn: angular.ILinkFn = null;
|
||||
directive: angular.IDirective = null;
|
||||
$controller: angular.IControllerService = null;
|
||||
|
||||
constructor(public name: string) {
|
||||
var selector = name.replace(CAMEL_CASE, (all, next: string) => '-' + next.toLowerCase());
|
||||
var self = this;
|
||||
this.type =
|
||||
Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename})
|
||||
.Class({
|
||||
constructor: [
|
||||
new Inject(NG1_SCOPE),
|
||||
ElementRef,
|
||||
function(scope: angular.IScope, elementRef: ElementRef) {
|
||||
return new UpgradeNg1ComponentAdapter(
|
||||
self.linkFn, scope, self.directive, elementRef, self.$controller, self.inputs,
|
||||
self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
||||
}
|
||||
],
|
||||
ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ },
|
||||
ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ },
|
||||
ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ }
|
||||
});
|
||||
}
|
||||
|
||||
extractDirective(injector: angular.IInjectorService): angular.IDirective {
|
||||
var directives: angular.IDirective[] = injector.get(this.name + 'Directive');
|
||||
if (directives.length > 1) {
|
||||
throw new Error('Only support single directive definition for: ' + this.name);
|
||||
}
|
||||
var directive = directives[0];
|
||||
if (directive.replace) this.notSupported('replace');
|
||||
if (directive.terminal) this.notSupported('terminal');
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object') {
|
||||
if ((<angular.IDirectivePrePost>link).post) this.notSupported('link.post');
|
||||
}
|
||||
return directive;
|
||||
}
|
||||
|
||||
private notSupported(feature: string) {
|
||||
throw new Error(`Upgraded directive '${this.name}' does not support '${feature}'.`);
|
||||
}
|
||||
|
||||
extractBindings() {
|
||||
var btcIsObject = typeof this.directive.bindToController === 'object';
|
||||
if (btcIsObject && Object.keys(this.directive.scope).length) {
|
||||
throw new Error(
|
||||
`Binding definitions on scope and controller at the same time are not supported.`);
|
||||
}
|
||||
|
||||
var context = (btcIsObject) ? this.directive.bindToController : this.directive.scope;
|
||||
|
||||
if (typeof context == 'object') {
|
||||
for (var name in context) {
|
||||
if ((<any>context).hasOwnProperty(name)) {
|
||||
var localName = context[name];
|
||||
var type = localName.charAt(0);
|
||||
localName = localName.substr(1) || name;
|
||||
var outputName = 'output_' + name;
|
||||
var outputNameRename = outputName + ': ' + name;
|
||||
var outputNameRenameChange = outputName + ': ' + name + 'Change';
|
||||
var inputName = 'input_' + name;
|
||||
var inputNameRename = inputName + ': ' + name;
|
||||
switch (type) {
|
||||
case '=':
|
||||
this.propertyOutputs.push(outputName);
|
||||
this.checkProperties.push(localName);
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRenameChange);
|
||||
this.propertyMap[outputName] = localName;
|
||||
// don't break; let it fall through to '@'
|
||||
case '@':
|
||||
// handle the '<' binding of angular 1.5 components
|
||||
case '<':
|
||||
this.inputs.push(inputName);
|
||||
this.inputsRename.push(inputNameRename);
|
||||
this.propertyMap[inputName] = localName;
|
||||
break;
|
||||
case '&':
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRename);
|
||||
this.propertyMap[outputName] = localName;
|
||||
break;
|
||||
default:
|
||||
var json = JSON.stringify(context);
|
||||
throw new Error(
|
||||
`Unexpected mapping '${type}' in '${json}' in '${this.name}' directive.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileTemplate(compile: angular.ICompileService, templateCache: angular.ITemplateCacheService,
|
||||
httpBackend: angular.IHttpBackendService): Promise<any> {
|
||||
if (this.directive.template !== undefined) {
|
||||
this.linkFn = compileHtml(this.directive.template);
|
||||
} else if (this.directive.templateUrl) {
|
||||
var url = this.directive.templateUrl;
|
||||
var html = templateCache.get(url);
|
||||
if (html !== undefined) {
|
||||
this.linkFn = compileHtml(html);
|
||||
} else {
|
||||
return new Promise((resolve, err) => {
|
||||
httpBackend('GET', url, null, (status, response) => {
|
||||
if (status == 200) {
|
||||
resolve(this.linkFn = compileHtml(templateCache.put(url, response)));
|
||||
} else {
|
||||
err(`GET ${url} returned ${status}: ${response}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Directive '${this.name}' is not a component, it is missing template.`);
|
||||
}
|
||||
return null;
|
||||
function compileHtml(html): angular.ILinkFn {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return compile(div.childNodes);
|
||||
}
|
||||
}
|
||||
|
||||
static resolve(exportedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder},
|
||||
injector: angular.IInjectorService): Promise<any> {
|
||||
var promises = [];
|
||||
var compile: angular.ICompileService = injector.get(NG1_COMPILE);
|
||||
var templateCache: angular.ITemplateCacheService = injector.get(NG1_TEMPLATE_CACHE);
|
||||
var httpBackend: angular.IHttpBackendService = injector.get(NG1_HTTP_BACKEND);
|
||||
var $controller: angular.IControllerService = injector.get(NG1_CONTROLLER);
|
||||
for (var name in exportedComponents) {
|
||||
if ((<any>exportedComponents).hasOwnProperty(name)) {
|
||||
var exportedComponent = exportedComponents[name];
|
||||
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
||||
exportedComponent.$controller = $controller;
|
||||
exportedComponent.extractBindings();
|
||||
var promise = exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
||||
if (promise) promises.push(promise);
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
}
|
||||
|
||||
class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
|
||||
destinationObj: any = null;
|
||||
checkLastValues: any[] = [];
|
||||
componentScope: angular.IScope;
|
||||
element: Element;
|
||||
|
||||
constructor(private linkFn: angular.ILinkFn, scope: angular.IScope,
|
||||
private directive: angular.IDirective, elementRef: ElementRef,
|
||||
$controller: angular.IControllerService, private inputs: string[],
|
||||
private outputs: string[], private propOuts: string[],
|
||||
private checkProperties: string[], private propertyMap: {[key: string]: string}) {
|
||||
this.element = elementRef.nativeElement;
|
||||
this.componentScope = scope.$new(!!directive.scope);
|
||||
var $element = angular.element(this.element);
|
||||
var controllerType = directive.controller;
|
||||
var controller: any = null;
|
||||
if (controllerType) {
|
||||
var locals = {$scope: this.componentScope, $element: $element};
|
||||
controller = $controller(controllerType, locals, null, directive.controllerAs);
|
||||
$element.data(controllerKey(directive.name), controller);
|
||||
}
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object') link = (<angular.IDirectivePrePost>link).pre;
|
||||
if (link) {
|
||||
var attrs: angular.IAttributes = NOT_SUPPORTED;
|
||||
var transcludeFn: angular.ITranscludeFunction = NOT_SUPPORTED;
|
||||
var linkController = this.resolveRequired($element, directive.require);
|
||||
(<angular.IDirectiveLinkFn>directive.link)(this.componentScope, $element, attrs,
|
||||
linkController, transcludeFn);
|
||||
}
|
||||
this.destinationObj =
|
||||
directive.bindToController && controller ? controller : this.componentScope;
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
this[inputs[i]] = null;
|
||||
}
|
||||
for (var j = 0; j < outputs.length; j++) {
|
||||
var emitter = this[outputs[j]] = new EventEmitter();
|
||||
this.setComponentProperty(outputs[j], ((emitter) => (value) => emitter.emit(value))(emitter));
|
||||
}
|
||||
for (var k = 0; k < propOuts.length; k++) {
|
||||
this[propOuts[k]] = new EventEmitter();
|
||||
this.checkLastValues.push(INITIAL_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
var childNodes: Node[] = [];
|
||||
var childNode;
|
||||
while (childNode = this.element.firstChild) {
|
||||
this.element.removeChild(childNode);
|
||||
childNodes.push(childNode);
|
||||
}
|
||||
this.linkFn(this.componentScope, (clonedElement: Node[], scope: angular.IScope) => {
|
||||
for (var i = 0, ii = clonedElement.length; i < ii; i++) {
|
||||
this.element.appendChild(clonedElement[i]);
|
||||
}
|
||||
}, {parentBoundTranscludeFn: (scope, cloneAttach) => { cloneAttach(childNodes); }});
|
||||
if (this.destinationObj.$onInit) {
|
||||
this.destinationObj.$onInit();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}) {
|
||||
for (var name in changes) {
|
||||
if ((<Object>changes).hasOwnProperty(name)) {
|
||||
var change: SimpleChange = changes[name];
|
||||
this.setComponentProperty(name, change.currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): number {
|
||||
var count = 0;
|
||||
var destinationObj = this.destinationObj;
|
||||
var lastValues = this.checkLastValues;
|
||||
var checkProperties = this.checkProperties;
|
||||
for (var i = 0; i < checkProperties.length; i++) {
|
||||
var value = destinationObj[checkProperties[i]];
|
||||
var last = lastValues[i];
|
||||
if (value !== last) {
|
||||
if (typeof value == 'number' && isNaN(value) && typeof last == 'number' && isNaN(last)) {
|
||||
// ignore because NaN != NaN
|
||||
} else {
|
||||
var eventEmitter: EventEmitter<any> = this[this.propOuts[i]];
|
||||
eventEmitter.emit(lastValues[i] = value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
setComponentProperty(name: string, value: any) {
|
||||
this.destinationObj[this.propertyMap[name]] = value;
|
||||
}
|
||||
|
||||
private resolveRequired($element: angular.IAugmentedJQuery, require: string | string[]): any {
|
||||
if (!require) {
|
||||
return undefined;
|
||||
} else if (typeof require == 'string') {
|
||||
var name: string = <string>require;
|
||||
var isOptional = false;
|
||||
var startParent = false;
|
||||
var searchParents = false;
|
||||
var ch: string;
|
||||
if (name.charAt(0) == '?') {
|
||||
isOptional = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
searchParents = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
startParent = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
|
||||
var key = controllerKey(name);
|
||||
if (startParent) $element = $element.parent();
|
||||
var dep = searchParents ? $element.inheritedData(key) : $element.data(key);
|
||||
if (!dep && !isOptional) {
|
||||
throw new Error(`Can not locate '${require}' in '${this.directive.name}'.`);
|
||||
}
|
||||
return dep;
|
||||
} else if (require instanceof Array) {
|
||||
var deps = [];
|
||||
for (var i = 0; i < require.length; i++) {
|
||||
deps.push(this.resolveRequired($element, require[i]));
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
throw new Error(
|
||||
`Directive '${this.directive.name}' require syntax unrecognized: ${this.directive.require}`);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user