diff --git a/README.md b/README.md index 69d36d986c..4b82430bcf 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,25 @@ Angular.io is currently the preview site for Angular 2. This site also includes 3. run `harp server` 4. Open this url in the browser: [http://localhost:9000/](http://localhost:9000/) +## Development setup with watches + 1. cd into root directory `angular.io/` + 2. run `gulp serve-and-watch` + 3. Open this url in the browser: [http://localhost:9000/](http://localhost:9000/) + 4. Refresh your browser to see any changes. + +## Development setup with watches and browser reload + 1. cd into root directory `angular.io/` + 2. install `browser-sync` + + `npm install -g browser-sync`
+ + *or on Windows*
+ + `npm install -g browser-sync --msvs_version=2013` + + 3. run `gulp serve-and-watch` + 4. run `browser-sync start --proxy localhost:9000 --files "public/docs/**/*/**/*" --reloadDelay 500` + 5. browser will launch and stay refreshed automatically. ## Technology Used - Angular 1.x: The production ready version of Angular diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000000..d2f49526cb --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,76 @@ +var gulp = require('gulp'); +var watch = require('gulp-watch'); +var gutil = require('gulp-util'); +var Dgeni = require('dgeni'); +var path = require('path'); +var del = require('del'); + +var docShredder = require('./public/doc-shredder/doc-shredder'); + +var shredOptions = { + basePath: path.resolve('./public/docs'), + sourceDir: "_examples", + destDir: "_fragments" +}; + +gulp.task('shred-full', ['shred-clean'], function() { + docShredder.shred( shredOptions); +}); + +gulp.task('serve-and-watch', function (cb) { + var pattern = path.join(shredOptions.basePath, shredOptions.sourceDir, "**/*.*"); + + execCommands(['harp server']) + + watch([ pattern], function(event, done) { + console.log('Event type: ' + event.event); // added, changed, or deleted + console.log('Event path: ' + event.path); // The path of the modified file + docShredder.shredSingleDir(shredOptions, event.path); + }); + +}); + +gulp.task('shred-clean', function(cb) { + var cleanPath = path.join(shredOptions.basePath, shredOptions.destDir, '**/*.*') + del([ cleanPath, '!**/*.ovr.*'], function (err, paths) { + // console.log('Deleted files/folders:\n', paths.join('\n')); + cb(); + }); +}); + + +// added options are: shouldLog +// cb is function(err, stdout, stderr); +function execCommands(cmds, options, cb) { + options = options || {}; + options.shouldThrow = options.shouldThrow == null ? true : options.shouldThrow; + options.shouldLog = options.shouldLog == null ? true : options.shouldLog; + if (!cmds || cmds.length == 0) cb(null, null, null); + var exec = require('child_process').exec; // just to make it more portable. + exec(cmds[0], options, function(err, stdout, stderr) { + if (err == null) { + if (options.shouldLog) { + gutil.log('cmd: ' + cmds[0]); + gutil.log('stdout: ' + stdout); + } + if (cmds.length == 1) { + cb(err, stdout, stderr); + } else { + execCommands(cmds.slice(1), options, cb); + } + } else { + if (options.shouldLog) { + gutil.log('exec error on cmd: ' + cmds[0]); + gutil.log('exec error: ' + err); + if (stdout) gutil.log('stdout: ' + stdout); + if (stderr) gutil.log('stderr: ' + stderr); + } + if (err && options.shouldThrow) throw err; + cb(err, stdout, stderr); + } + }); +} + + + +gulp.task('default', ['shred']); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000..84ab91fab5 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "angular.io", + "version": "0.0.0", + "private": true, + "description": "Angular 2 documentation", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git" + }, + "licenses": [ + { + "type": "Apache", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], + "bugs": { + "url": "" + }, + "devDependencies": { + "canonical-path": "0.0.2", + "del": "^1.2.0", + "dgeni": "^0.4.0", + "dgeni-packages": "^0.10.0", + "gulp": "^3.5.6", + "gulp-util": "^3.0.6", + "gulp-watch": "^4.3.4", + "lodash": "^3.10.1", + "path": "^0.11.14" + }, + "contributors": [ + "Jay Traband " + ] +} diff --git a/public/_includes/_util-fns.jade b/public/_includes/_util-fns.jade new file mode 100644 index 0000000000..cd26fa57da --- /dev/null +++ b/public/_includes/_util-fns.jade @@ -0,0 +1,35 @@ +- var getFrag = function(fileName) { +- var frag = partial(fileName); +- if (frag == null) { +- return "BAD FILENAME: " + fileName + " Current path: " + current.path; +- } else { +- // ``` gets translated to
.....
and we need +- // to remove this from the fragment prefix is 11 long and suffix is 13 long +- var r = frag.substring(11, frag.length-13); +- return r; +- } +- } + +- var getExtn = function(fileName) { +- var ix = fileName.lastIndexOf('.'); +- return ix > 0 ? fileName.substr(ix+1) : ""; +- } + +// HACK: to avoid having to include a path in makeTabs calls +- var currentPath = current.path; +// need to back up to 'docs' +- var pathToFrags = "../../../../../../../../../../../".substr(0, (currentPath.length-2)*3) + "_fragments/"; + +mixin makeTabs(path, fileNames, tabNames) + - fileNames = fileNames.split(","); + - tabNames = tabNames.split(",") + // .p Length #{currentPath.length} + code-tabs + each fileName,index in fileNames + - var tabName = tabNames[index].trim(); + - var fileName = fileNames[index].trim(); + - var extn = getExtn(fileName); + // - var extPath = pathToFrags + (path.length ? path + "/" : ""); + - var extPath = pathToFrags + path + "/"; + code-pane(language="#{extn}" name="#{tabName}" format="linenums") + != getFrag(extPath + fileName + ".md") diff --git a/public/doc-shredder/doc-shredder.js b/public/doc-shredder/doc-shredder.js new file mode 100644 index 0000000000..d1c20635ba --- /dev/null +++ b/public/doc-shredder/doc-shredder.js @@ -0,0 +1,136 @@ + +// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes +var path = require('canonical-path'); +// var path = require('path'); +var del = require('del'); +var Dgeni = require('dgeni'); +var _ = require('lodash'); + +var createPackage = function(shredOptions) { + var shredder = new Dgeni.Package('doc-shredder', [ + // require('dgeni-packages/base') - doesn't work + ]); + shredder.options = resolveOptions(shredOptions); + return configure(shredder); +}; + +var resolveOptions = function(shredOptions) { + return _.defaults({}, shredOptions, { + basePath: path.resolve('.'), + // read files from any subdir under here + sourceDir: "docs/_examples", + // shredded files get copied here with same subdir structure. + destDir: "docs/_fragments", + // whether to include subdirectories when shredding. + includeSubdirs: true + }); +} + +var shred = function(shredOptions) { + try { + var pkg = createPackage(shredOptions); + var dgeni = new Dgeni([ pkg]); + return dgeni.generate(); + } catch(x) { + console.log(x.stack); + throw x; + } +} + +var shredSingleDir = function(shredOptions, filePath) { + shredOptions = resolveOptions(shredOptions); + var root = path.resolve(shredOptions.basePath, shredOptions.sourceDir); + var fileDir = path.dirname(filePath); + var relativePath = path.relative(root, fileDir); + var sourceDir = path.join(shredOptions.sourceDir, relativePath); + var destDir = path.join(shredOptions.destDir, relativePath); + var options = { + basePath: shredOptions.basePath, + includeSubdirs: false, + sourceDir: sourceDir, + destDir: destDir + } + var cleanPath = path.join(shredOptions.basePath, destDir, '*.*') + del([ cleanPath, '!**/*.ovr.*'], function (err, paths) { + // console.log('Deleted files/folders:\n', paths.join('\n')); + return shred(options); + }); + +} + +module.exports = { + shred: shred, + shredSingleDir: shredSingleDir, + createPackage: createPackage, + resolveOptions: resolveOptions +}; + +function configure(shredder) { + var options = shredder.options; + shredder + .processor(require('dgeni-packages/base/processors/read-files')) + .processor(require('dgeni-packages/base/processors/write-files')) + .factory(require('dgeni-packages/base/services/writefile')) + + // Ugh... Boilerplate that dgeni needs to sequence operations + .processor({ name: 'reading-files' }) + .processor({ name: 'files-read', $runAfter: ['reading-files'] }) + .processor({ name: 'processing-docs', $runAfter: ['files-read'] }) + .processor({ name: 'docs-processed', $runAfter: ['processing-docs'] }) + .processor({ name: 'adding-extra-docs', $runAfter: ['docs-processed'] }) + .processor({ name: 'extra-docs-added', $runAfter: ['adding-extra-docs'] }) + .processor({ name: 'computing-ids', $runAfter: ['extra-docs-added'] }) + .processor({ name: 'ids-computed', $runAfter: ['computing-ids'] }) + .processor({ name: 'computing-paths', $runAfter: ['ids-computed'] }) + .processor({ name: 'paths-computed', $runAfter: ['computing-paths'] }) + .processor({ name: 'rendering-docs', $runAfter: ['paths-computed'] }) + .processor({ name: 'docs-rendered', $runAfter: ['rendering-docs'] }) + .processor({ name: 'writing-files', $runAfter: ['docs-rendered'] }) + .processor({ name: 'files-written', $runAfter: ['writing-files'] }) + + .factory(require('./fileShredder')) + .factory(require('./regionExtractor')) + .processor(require('./mdWrapperProcessor')) + + .config(function(log) { + // Set logging level + log.level = 'info'; + }) + + + .config(function(readFilesProcessor, fileShredder ) { + readFilesProcessor.fileReaders = [ fileShredder]; + }) + + // default configs - may be overriden + .config(function(readFilesProcessor) { + + // Specify the base path used when resolving relative paths to source and output files + readFilesProcessor.basePath = options.basePath; + + // Specify collections of source files that should contain the documentation to extract + var extns = ['*.js', '*.html', '*.ts', '*.css' ]; + var includeFiles = extns.map(function(extn) { + if (options.includeSubdirs) { + return path.join(options.sourceDir, '**', extn); + } else { + return path.join(options.sourceDir, extn); + } + }); + readFilesProcessor.sourceFiles = [ + { + // Process all candidate files in `src` and its subfolders ... + include: includeFiles, + + // When calculating the relative path to these files use this as the base path. + // So `src/foo/bar.js` will have relative path of `foo/bar.js` + basePath: options.sourceDir + } + ]; + }) + .config(function(writeFilesProcessor) { + // Specify where the writeFilesProcessor will write our generated doc files + writeFilesProcessor.outputFolder = options.destDir; + }); + return shredder; +} diff --git a/public/doc-shredder/fileShredder.js b/public/doc-shredder/fileShredder.js new file mode 100644 index 0000000000..6c3590334b --- /dev/null +++ b/public/doc-shredder/fileShredder.js @@ -0,0 +1,30 @@ +/** + * @dgService htmlFileShredder + * @description + */ +module.exports = function fileShredder(log, regionExtractor) { + return { + name: 'fileShredder', + + getDocs: function (fileInfo) { + var commentMarkers; + switch (fileInfo.extension) { + case 'ts': + case 'js': + commentMarkers = ['//']; + break; + case 'html': + commentMarkers = [' + + + - 2) Region syntax for js/ts + + // #docregion main + + // #enddocregion + +# typescript compiler call + tsc --m commonjs --t es5 --emitDecoratorMetadata --experimentalDecorators --sourceMap app.ts + \ No newline at end of file diff --git a/public/doc-shredder/regionExtractor.js b/public/doc-shredder/regionExtractor.js new file mode 100644 index 0000000000..07557b46dd --- /dev/null +++ b/public/doc-shredder/regionExtractor.js @@ -0,0 +1,71 @@ +module.exports = function regionExtractor() { + // split out each fragment in {content} into a separate doc + // a fragment is a section of text surrounded by + // 1) In front: a comment marker followed by '#docregion' followed by an optional region name. For example: + // <-- #docregion foo --> for html + // or // #docregion foo for js/ts + // 2) In back: a comment marker followed by '#enddocregion' + // Regions can be nested and any regions not 'closed' are automatically closed at the end of the doc. + return function(content, commentPrefixes) { + + var lines = result = content.split(/\r?\n/); + var docs = []; + var docStack = []; + var doc = null; + var nullLine = '###'; + var rx = new RegExp(nullLine + '\n', 'g'); + lines.forEach(function(line, ix) { + if (isCommentLine(line, commentPrefixes)) { + if (hasRegionTag(line)) { + doc = {startIx: ix, regionName: getRegionName(line)}; + lines[ix] = nullLine; + docs.push(doc); + docStack.push(doc); + } else if (hasEndRegionTag(line)) { + lines[ix] = nullLine; + doc.endIx = ix; + doc = docStack.pop(); + } + } + }); + + docs.forEach(function(doc) { + var content; + if (doc.endIx) { + content = lines.slice(doc.startIx + 1, doc.endIx).join('\n'); + } else { + content = lines.slice(doc.startIx + 1).join('\n'); + } + // eliminate all #docregion lines + doc.content = content.replace(rx, ''); + + }); + return docs; + } + +}; + +function isCommentLine(line, commentPrefixes) { + return commentPrefixes.some(function(prefix) { + return line.trim().indexOf(prefix) == 0; + }); +} + +function hasRegionTag(line) { + return line.indexOf("#docregion") >= 0; +} + +function hasEndRegionTag(line) { + return line.indexOf("#enddocregion") >= 0; +} + +function getRegionName(line) { + try { + var name = line.match(/#docregion\s*(\S*).*/)[1]; + // Hack for html regions that look like or */ + name = name.replace("-->","").replace('\*\/',""); + return name; + } catch (e) { + return ''; + } +} diff --git a/public/doc-shredder/test/.gitignore b/public/doc-shredder/test/.gitignore new file mode 100644 index 0000000000..52ed488544 --- /dev/null +++ b/public/doc-shredder/test/.gitignore @@ -0,0 +1,18 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +test_fragments + +pids +logs +results + +npm-debug.log +node_modules + +build \ No newline at end of file diff --git a/public/doc-shredder/test/LICENSE b/public/doc-shredder/test/LICENSE new file mode 100644 index 0000000000..ad410e1130 --- /dev/null +++ b/public/doc-shredder/test/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/public/doc-shredder/test/gulpfile.js b/public/doc-shredder/test/gulpfile.js new file mode 100644 index 0000000000..07cd4e93e6 --- /dev/null +++ b/public/doc-shredder/test/gulpfile.js @@ -0,0 +1,37 @@ +var gulp = require('gulp'); +var path = require('canonical-path'); +var Dgeni = require('dgeni'); +var del = require('del'); +var watch = require('gulp-watch'); + +var docShredder = require('../doc-shredder'); + +var shredOptions = docShredder.resolveOptions({ + sourceDir: "test_source", + destDir: "test_fragments" +}); + +gulp.task('shred', function() { + return docShredder.shred(shredOptions); +}); + +gulp.task('clean', function (cb) { + var cleanPath = path.join(shredOptions.destDir, '**/*.*') + del([ cleanPath, '!**/*.ovr.*'], function (err, paths) { + // console.log('Deleted files/folders:\n', paths.join('\n')); + cb(); + }); +}); + +gulp.task('watch', function (cb) { + var pattern = path.join(shredOptions.sourceDir, "**/*.*"); + watch([ pattern], function(event, done) { + console.log('Event type: ' + event.event); // added, changed, or deleted + console.log('Event path: ' + event.path); // The path of the modified file + docShredder.shredSingleDir(shredOptions, event.path); + }); +}); + + +gulp.task('default', ['shred']); + diff --git a/public/doc-shredder/test/package.json b/public/doc-shredder/test/package.json new file mode 100644 index 0000000000..3374cba03b --- /dev/null +++ b/public/doc-shredder/test/package.json @@ -0,0 +1,35 @@ +{ + "name": "test-shredder", + "version": "0.0.0", + "private": true, + "description": "Documentation file shredder", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git" + }, + "author": "Jay Traband", + "licenses": [ + { + "type": "Apache", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], + "bugs": { + "url": "" + }, + "devDependencies": { + "canonical-path": "0.0.2", + "del": "^1.2.0", + "dgeni": "^0.4.0", + "dgeni-packages": "^0.10.0", + "gulp": "^3.5.6", + "gulp-watch": "^4.3.4", + "path": "^0.11.14" + }, + "contributors": [ + "Jay Traband" + ] +} diff --git a/public/doc-shredder/test/test_fragments/first.ovr.app.html.md b/public/doc-shredder/test/test_fragments/first.ovr.app.html.md new file mode 100644 index 0000000000..9d24c9b176 --- /dev/null +++ b/public/doc-shredder/test/test_fragments/first.ovr.app.html.md @@ -0,0 +1,5 @@ +``` + + + +``` \ No newline at end of file diff --git a/public/doc-shredder/test/test_fragments/first.ovr.script.html.md b/public/doc-shredder/test/test_fragments/first.ovr.script.html.md new file mode 100644 index 0000000000..a47cb833da --- /dev/null +++ b/public/doc-shredder/test/test_fragments/first.ovr.script.html.md @@ -0,0 +1,4 @@ +``` + + +``` \ No newline at end of file diff --git a/public/doc-shredder/test/test_fragments/sub1/script.ovr.js.md b/public/doc-shredder/test/test_fragments/sub1/script.ovr.js.md new file mode 100644 index 0000000000..9d39fe0eb3 --- /dev/null +++ b/public/doc-shredder/test/test_fragments/sub1/script.ovr.js.md @@ -0,0 +1,11 @@ +``` +/** + * @description This function returns a string. + * + * @returns {string} This string has the value 'Hello World'. + */ + +function helloWorld() { + return 'Hello World'; +} +``` \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/app.js b/public/doc-shredder/test/test_source/app.js new file mode 100644 index 0000000000..52b5663ad4 --- /dev/null +++ b/public/doc-shredder/test/test_source/app.js @@ -0,0 +1,24 @@ +// #docregion all +// #docregion log ... everything else ignored. +/** + * @description This function logs a string. + */ +function log() { + console.log('Logging.'); +} +// #enddocregion + +/** + * @description My application + */ +var myApp = { + // #docregion greet + /** + * @description Display a greeting + * @param {string} name The name of the person to greet + */ + greet: function(name) { + console.log('hello ' + name); + } + // #enddocregion +}; \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/do-not-read.js b/public/doc-shredder/test/test_source/do-not-read.js new file mode 100644 index 0000000000..de18a65e84 --- /dev/null +++ b/public/doc-shredder/test/test_source/do-not-read.js @@ -0,0 +1,5 @@ +/** + * @description + * This file should not have documents read from it. + * + */ \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/first.html b/public/doc-shredder/test/test_source/first.html new file mode 100644 index 0000000000..b85c224a59 --- /dev/null +++ b/public/doc-shredder/test/test_source/first.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/doc-shredder/test/test_source/sub1/foo/script.js b/public/doc-shredder/test/test_source/sub1/foo/script.js new file mode 100644 index 0000000000..8cc7201a66 --- /dev/null +++ b/public/doc-shredder/test/test_source/sub1/foo/script.js @@ -0,0 +1,11 @@ +// #docregion +/** + * @description This function returns a string. + * + * @returns {string} This string has the value 'Hello World'. + */ + + +function helloWorld() { + return 'Hello World'; +} \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/sub1/script.2.js b/public/doc-shredder/test/test_source/sub1/script.2.js new file mode 100644 index 0000000000..a76e425917 --- /dev/null +++ b/public/doc-shredder/test/test_source/sub1/script.2.js @@ -0,0 +1,9 @@ +// #docregion +/** + * @description This function returns a string. + * + * @returns {string} This string has the value 'Hello World'. + */ +function helloWorld() { + return 'Hello World'; +} \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/sub1/script.js b/public/doc-shredder/test/test_source/sub1/script.js new file mode 100644 index 0000000000..8cc7201a66 --- /dev/null +++ b/public/doc-shredder/test/test_source/sub1/script.js @@ -0,0 +1,11 @@ +// #docregion +/** + * @description This function returns a string. + * + * @returns {string} This string has the value 'Hello World'. + */ + + +function helloWorld() { + return 'Hello World'; +} \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/sub1/script.ts b/public/doc-shredder/test/test_source/sub1/script.ts new file mode 100644 index 0000000000..1e2b501fc2 --- /dev/null +++ b/public/doc-shredder/test/test_source/sub1/script.ts @@ -0,0 +1,10 @@ +// #docregion +/** + * @description This function returns a string. + * + * @returns {string} This string has the value 'Hello World'. + */ +// #docregion code2 +function helloWorld() { + return 'Hello World'; +} \ No newline at end of file diff --git a/public/doc-shredder/test/test_source/sub1/second.html b/public/doc-shredder/test/test_source/sub1/second.html new file mode 100644 index 0000000000..55449e0cde --- /dev/null +++ b/public/doc-shredder/test/test_source/sub1/second.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/public/docs/_examples/gettingstarted/js/index.html b/public/docs/_examples/gettingstarted/js/index.html new file mode 100644 index 0000000000..432b17eb52 --- /dev/null +++ b/public/docs/_examples/gettingstarted/js/index.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/docs/_examples/gettingstarted/js/main.js b/public/docs/_examples/gettingstarted/js/main.js new file mode 100644 index 0000000000..b752be57b4 --- /dev/null +++ b/public/docs/_examples/gettingstarted/js/main.js @@ -0,0 +1,17 @@ +// #docregion +function AppComponent() {} + +AppComponent.annotations = [ + new angular.ComponentAnnotation({ + selector: 'my-app' + }), + new angular.ViewAnnotation({ + template: '

My first Angular 2 App

' + }) +]; + +// #docregion bootstrap +document.addEventListener('DOMContentLoaded', function() { + angular.bootstrap(AppComponent); +}); +// #enddocregion \ No newline at end of file diff --git a/public/docs/_examples/gettingstarted/protractor-spec.js b/public/docs/_examples/gettingstarted/protractor-spec.js new file mode 100644 index 0000000000..2a7633774e --- /dev/null +++ b/public/docs/_examples/gettingstarted/protractor-spec.js @@ -0,0 +1,20 @@ +// protractor-spec.js +describe('Protractor quick start test', function() { + + // #docregion javascript + it('should display Alice with JavaScript', function() { + browser.get('gettingstarted/js/index.html'); + }); + // #enddocregion + + // #docregion typescript + it('should display Alice with TypeScrip', function() { + browser.get('gettingstarted/ts/index.html'); + }); + // #enddocregion + + afterEach(function() { + expect(element(by.id('output')).getText()).toEqual('My first Angular 2 App'); + }); +}); + diff --git a/public/docs/_examples/gettingstarted/ts/index.html b/public/docs/_examples/gettingstarted/ts/index.html new file mode 100644 index 0000000000..b44d34d016 --- /dev/null +++ b/public/docs/_examples/gettingstarted/ts/index.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/public/docs/_examples/gettingstarted/ts/main.js b/public/docs/_examples/gettingstarted/ts/main.js new file mode 100644 index 0000000000..77c82e8254 --- /dev/null +++ b/public/docs/_examples/gettingstarted/ts/main.js @@ -0,0 +1,33 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); + switch (arguments.length) { + case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); + case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); + case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); + } +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +// #docregion +// #docregion import +var angular2_1 = require('angular2/angular2'); +// #enddocregion +var AppComponent = (function () { + function AppComponent() { + } + AppComponent = __decorate([ + angular2_1.Component({ + selector: 'my-app' + }), + angular2_1.View({ + template: '

My first Angular 2 App

' + }), + __metadata('design:paramtypes', []) + ], AppComponent); + return AppComponent; +})(); +// #docregion bootstrap +angular2_1.bootstrap(AppComponent); +// #enddocregion +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/public/docs/_examples/gettingstarted/ts/main.js.map b/public/docs/_examples/gettingstarted/ts/main.js.map new file mode 100644 index 0000000000..a03477dbb1 --- /dev/null +++ b/public/docs/_examples/gettingstarted/ts/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":["AppComponent","AppComponent.constructor"],"mappings":";;;;;;;;;;;AAEA,AAFA,UAAU;AACV,iBAAiB;AACjB,yBAAyC,mBAAmB,CAAC,CAAA;AAG7D,AAFA,aAAa;;IAEbA;IAOAC,CAACA;IAPDD;QAACA,oBAASA,CAACA;YACTA,QAAQA,EAAEA,QAAQA;SACnBA,CAACA;QACDA,eAAIA,CAACA;YACJA,QAAQA,EAAEA,6CAA6CA;SACxDA,CAACA;;qBAEDA;IAADA,mBAACA;AAADA,CAACA,AAPD,IAOC;AAGD,AADA,oBAAoB;AACpB,oBAAS,CAAC,YAAY,CAAC,CAAC;AACxB,aAAa"} \ No newline at end of file diff --git a/public/docs/_examples/gettingstarted/ts/main.ts b/public/docs/_examples/gettingstarted/ts/main.ts new file mode 100644 index 0000000000..7cf1b4659a --- /dev/null +++ b/public/docs/_examples/gettingstarted/ts/main.ts @@ -0,0 +1,16 @@ +// #docregion +// #docregion import +import {Component, View, bootstrap} from 'angular2/angular2'; +// #enddocregion + +@Component({ + selector: 'my-app' +}) +@View({ + template: '

My first Angular 2 App

' +}) +class AppComponent { +} +// #docregion bootstrap +bootstrap(AppComponent); +// #enddocregion diff --git a/public/docs/_examples/protractor-conf.js b/public/docs/_examples/protractor-conf.js new file mode 100644 index 0000000000..d341244fa6 --- /dev/null +++ b/public/docs/_examples/protractor-conf.js @@ -0,0 +1,25 @@ +exports.config = { + onPrepare: function() { + patchProtractorWait(browser); + }, + seleniumAddress: 'http://localhost:4444/wd/hub', + baseUrl: 'http://localhost:8080/', + specs: [ + 'quickstart/protractor-spec.js', + 'gettingstarted/protractor-spec.js' + ] +}; + +// Disable waiting for Angular as we don't have an integration layer yet... +// TODO(tbosch): Implement a proper debugging API for Ng2.0, remove this here +// and the sleeps in all tests. +function patchProtractorWait(browser) { + browser.ignoreSynchronization = true; + var _get = browser.get; + var sleepInterval = process.env.TRAVIS || process.env.JENKINS_URL ? 14000 : 8000; + browser.get = function() { + var result = _get.apply(this, arguments); + browser.sleep(sleepInterval); + return result; + } +} \ No newline at end of file diff --git a/public/docs/_examples/quickstart/app.js b/public/docs/_examples/quickstart/app.js new file mode 100644 index 0000000000..47783479c3 --- /dev/null +++ b/public/docs/_examples/quickstart/app.js @@ -0,0 +1,34 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); + switch (arguments.length) { + case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); + case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); + case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); + } +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +// #docregion +// #docregion import +var angular2_1 = require('angular2/angular2'); +// #enddocregion +var MyAppComponent = (function () { + function MyAppComponent() { + this.name = 'Alice'; + } + MyAppComponent = __decorate([ + angular2_1.Component({ + selector: 'my-app' + }), + angular2_1.View({ + template: '

Hello {{ name }}

' + }), + __metadata('design:paramtypes', []) + ], MyAppComponent); + return MyAppComponent; +})(); +// #docregion bootstrap +angular2_1.bootstrap(MyAppComponent); +// #enddocregion +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/public/docs/_examples/quickstart/app.js.map b/public/docs/_examples/quickstart/app.js.map new file mode 100644 index 0000000000..879e6ea4cd --- /dev/null +++ b/public/docs/_examples/quickstart/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":["MyAppComponent","MyAppComponent.constructor"],"mappings":";;;;;;;;;;;AAEA,AAFA,UAAU;AACV,iBAAiB;AACjB,yBAAyC,mBAAmB,CAAC,CAAA;AAG7D,AAFA,aAAa;;IAWZA;QACCC,IAAIA,CAACA,IAAIA,GAAGA,OAAOA,CAACA;IACrBA,CAACA;IAXFD;QAACA,oBAASA,CAACA;YACVA,QAAQA,EAAEA,QAAQA;SAClBA,CAACA;QACDA,eAAIA,CAACA;YACLA,QAAQA,EAAEA,uCAAuCA;SACjDA,CAACA;;uBAODA;IAADA,qBAACA;AAADA,CAACA,AAZD,IAYC;AAGD,AADA,oBAAoB;AACpB,oBAAS,CAAC,cAAc,CAAC,CAAC;AAC1B,aAAa"} \ No newline at end of file diff --git a/public/docs/_examples/quickstart/app.ts b/public/docs/_examples/quickstart/app.ts new file mode 100644 index 0000000000..19bdebff7e --- /dev/null +++ b/public/docs/_examples/quickstart/app.ts @@ -0,0 +1,22 @@ +// #docregion +// #docregion import +import {Component, View, bootstrap} from 'angular2/angular2'; +// #enddocregion + +@Component({ + selector: 'my-app' +}) +@View({ + template: '

Hello {{ name }}

' +}) +class MyAppComponent { + name : string; + + constructor() { + this.name = 'Alice'; + } +} + +// #docregion bootstrap +bootstrap(MyAppComponent); +// #enddocregion diff --git a/public/docs/_examples/quickstart/index.html b/public/docs/_examples/quickstart/index.html new file mode 100644 index 0000000000..9cb6fd586b --- /dev/null +++ b/public/docs/_examples/quickstart/index.html @@ -0,0 +1,15 @@ + + + + + Angular 2 Quickstart + + + + + + + + + + \ No newline at end of file diff --git a/public/docs/_examples/quickstart/protractor-spec.js b/public/docs/_examples/quickstart/protractor-spec.js new file mode 100644 index 0000000000..4d6e58fedd --- /dev/null +++ b/public/docs/_examples/quickstart/protractor-spec.js @@ -0,0 +1,13 @@ +// protractor-spec.js +describe('Protractor quick start test', function() { + beforeEach(function() { + browser.get('quickstart/index.html'); + }); + + // #docregion test + it('should display Alice', function() { + expect(element(by.id('output')).getText()).toEqual('Hello Alice'); + }); + // #enddocregion +}); + diff --git a/public/docs/_examples/tsconfig.json b/public/docs/_examples/tsconfig.json new file mode 100644 index 0000000000..dcd5faf918 --- /dev/null +++ b/public/docs/_examples/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "commonjs", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true + } +} \ No newline at end of file diff --git a/public/docs/_examples/typings/angular2/angular2.d.ts b/public/docs/_examples/typings/angular2/angular2.d.ts new file mode 100644 index 0000000000..dfad7d3494 --- /dev/null +++ b/public/docs/_examples/typings/angular2/angular2.d.ts @@ -0,0 +1,6137 @@ +// Type definitions for Angular v2.0.0-alpha.31 +// Project: http://angular.io/ +// Definitions by: angular team +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// *********************************************************** +// This file is generated by the Angular build process. +// Please do not create manual edits or send pull requests +// modifying this file. +// *********************************************************** + +// Angular depends transitively on these libraries. +// If you don't have them installed you can run +// $ tsd query es6-promise rx rx-lite --action install --save +/// +/// + +interface List extends Array {} +interface Map {} +interface StringMap extends Map {} + +declare module ng { + type SetterFn = typeof Function; + type int = number; + interface Type extends Function { + new (...args:any[]):any; + } + + // See https://github.com/Microsoft/TypeScript/issues/1168 + class BaseException /* extends Error */ { + message: string; + stack: string; + toString(): string; + } + interface InjectableReference {} +} + + + + +/** + * The `angular2` is the single place to import all of the individual types. + */ +declare module ng { + class DehydratedException extends BaseException { + } + + class ExpressionChangedAfterItHasBeenChecked extends BaseException { + } + + class ChangeDetectionError extends BaseException { + + location: string; + } + + + /** + * ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration. + */ + var ON_PUSH:any; + + + /** + * DEFAULT means that the change detector's mode will be set to CHECK_ALWAYS during hydration. + */ + var DEFAULT:any; + + + /** + * Controls change detection. + * + * {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It + * also allows detaching and + * attaching change detector subtrees. + */ + class ChangeDetectorRef { + + + /** + * Request to check all ON_PUSH ancestors. + */ + requestCheck(): void; + + + /** + * Detaches the change detector from the change detector tree. + * + * The detached change detector will not be checked until it is reattached. + */ + detach(): void; + + + /** + * Reattach the change detector to the change detector tree. + * + * This also requests a check of this change detector. This reattached change detector will be + * checked during the + * next change detection run. + */ + reattach(): void; + } + + class Pipes { + + + /** + * Map of {@link Pipe} names to {@link PipeFactory} lists used to configure the + * {@link Pipes} registry. + * + * #Example + * + * ``` + * var pipesConfig = { + * 'json': [jsonPipeFactory] + * } + * @Component({ + * viewInjector: [ + * bind(Pipes).toValue(new Pipes(pipesConfig)) + * ] + * }) + * ``` + */ + config: StringMap; + + get(type: string, obj: any, cdRef?: ChangeDetectorRef, existingPipe?: Pipe): Pipe; + } + + + /** + * Indicates that the result of a {@link Pipe} transformation has changed even though the reference + * has not changed. + * + * The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored. + */ + class WrappedValue { + + wrapped: any; + } + + + /** + * An interface for extending the list of pipes known to Angular. + * + * If you are writing a custom {@link Pipe}, you must extend this interface. + * + * #Example + * + * ``` + * class DoublePipe implements Pipe { + * supports(obj) { + * return true; + * } + * + * onDestroy() {} + * + * transform(value, args = []) { + * return `${value}${value}`; + * } + * } + * ``` + */ + interface Pipe { + + supports(obj: any): boolean; + + onDestroy(): void; + + transform(value: any, args: List): any; + } + + interface PipeFactory { + + supports(obs: any): boolean; + + create(cdRef: ChangeDetectorRef): Pipe; + } + + class NullPipe extends BasePipe { + + called: boolean; + + supports(obj: any): boolean; + + transform(value: any, args?: List): WrappedValue; + } + + class NullPipeFactory implements PipeFactory { + + supports(obj: any): boolean; + + create(cdRef: ChangeDetectorRef): Pipe; + } + + var defaultPipes : Pipes ; + + + /** + * Provides default implementation of supports and onDestroy. + * + * #Example + * + * ``` + * class DoublePipe extends BasePipe {* + * transform(value) { + * return `${value}${value}`; + * } + * } + * ``` + */ + class BasePipe implements Pipe { + + supports(obj: any): boolean; + + onDestroy(): void; + + transform(value: any, args: List): any; + } + + class Locals { + + parent: Locals; + + current: Map; + + contains(name: string): boolean; + + get(name: string): any; + + set(name: string, value: any): void; + + clearValues(): void; + } + + + interface AbstractControl_onlySelfArgs { + onlySelf?: boolean; + } + + interface AbstractControl_updateValueAndValidityArgs { + onlySelf?: boolean; + emitEvent?: boolean; + } + + /** + * Omitting from external API doc as this is really an abstract internal concept. + */ + class AbstractControl { + + validator: Function; + + value: any; + + status: string; + + valid: boolean; + + errors: StringMap; + + pristine: boolean; + + dirty: boolean; + + touched: boolean; + + untouched: boolean; + + valueChanges: Observable; + + markAsTouched(): void; + + markAsDirty(args?:AbstractControl_onlySelfArgs): void; + + setParent(parent: any): void; + + updateValidity(args?:AbstractControl_onlySelfArgs): void; + + updateValueAndValidity(args?:AbstractControl_updateValueAndValidityArgs): void; + + find(path: List| string): AbstractControl; + + getError(errorCode: string, path?: List): any; + + hasError(errorCode: string, path?: List): boolean; + } + + class AbstractControlDirective { + + control: AbstractControl; + + value: any; + + valid: boolean; + + errors: StringMap; + + pristine: boolean; + + dirty: boolean; + + touched: boolean; + + untouched: boolean; + } + + + interface Control_updateValueOptions { + onlySelf?: boolean; + emitEvent?: boolean; + } + /** + * Defines a part of a form that cannot be divided into other controls. + * + * `Control` is one of the three fundamental building blocks used to define forms in Angular, along + * with + * {@link ControlGroup} and {@link ControlArray}. + */ + class Control extends AbstractControl { + + updateValue(value: any, options?: Control_updateValueOptions): void; + + registerOnChange(fn: Function): void; + } + + + /** + * Defines a part of a form, of fixed length, that can contain other controls. + * + * A ControlGroup aggregates the values and errors of each {@link Control} in the group. Thus, if + * one of the controls + * in a group is invalid, the entire group is invalid. Similarly, if a control changes its value, + * the entire group + * changes as well. + * + * `ControlGroup` is one of the three fundamental building blocks used to define forms in Angular, + * along with + * {@link Control} and {@link ControlArray}. {@link ControlArray} can also contain other controls, + * but is of variable + * length. + */ + class ControlGroup extends AbstractControl { + + controls: StringMap; + + addControl(name: string, c: AbstractControl): void; + + removeControl(name: string): void; + + include(controlName: string): void; + + exclude(controlName: string): void; + + contains(controlName: string): boolean; + } + + + /** + * Defines a part of a form, of variable length, that can contain other controls. + * + * A `ControlArray` aggregates the values and errors of each {@link Control} in the group. Thus, if + * one of the controls + * in a group is invalid, the entire group is invalid. Similarly, if a control changes its value, + * the entire group + * changes as well. + * + * `ControlArray` is one of the three fundamental building blocks used to define forms in Angular, + * along with {@link Control} and {@link ControlGroup}. {@link ControlGroup} can also contain + * other controls, but is of fixed length. + */ + class ControlArray extends AbstractControl { + + controls: List; + + at(index: number): AbstractControl; + + push(control: AbstractControl): void; + + insert(index: number, control: AbstractControl): void; + + removeAt(index: number): void; + + length: number; + } + + + /** + * Creates and binds a control with a specified name to a DOM element. + * + * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}. + * + * # Example + * + * In this example, we create the login and password controls. + * We can work with each control separately: check its validity, get its value, listen to its + * changes. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ * Login + *
Login is invalid
+ * + * Password + * + * + *
+ * `}) + * class LoginComp { + * onLogIn(value) { + * // value === {login: 'some login', password: 'some password'} + * } + * } + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ * Login + * Password + * + *
+ * `}) + * class LoginComp { + * credentials: {login:string, password:string}; + * + * onLogIn() { + * // this.credentials.login === "some login" + * // this.credentials.password === "some password" + * } + * } + * ``` + */ + class NgControlName extends NgControl { + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: StringMap): void; + + onDestroy(): void; + + viewToModelUpdate(newValue: any): void; + + path: List; + + formDirective: any; + + control: Control; + + validator: Function; + } + + + /** + * Binds an existing control to a DOM element. + * + * # Example + * + * In this example, we bind the control to an input element. When the value of the input element + * changes, the value of + * the control will reflect that change. Likewise, if the value of the control changes, the input + * element reflects that + * change. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "" + * }) + * class LoginComp { + * loginControl:Control; + * + * constructor() { + * this.loginControl = new Control(''); + * } + * } + * + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "" + * }) + * class LoginComp { + * loginControl:Control; + * login:string; + * + * constructor() { + * this.loginControl = new Control(''); + * } + * } + * ``` + */ + class NgFormControl extends NgControl { + + form: Control; + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: any): void; + + path: List; + + control: Control; + + validator: Function; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * Binds a domain model to the form. + * + * # Example + * ``` + * @Component({selector: "search-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + * + * `}) + * class SearchComp { + * searchQuery: string; + * } + * ``` + */ + class NgModel extends NgControl { + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: any): void; + + control: Control; + + path: List; + + validator: Function; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * An abstract class that all control directive extend. + * + * It binds a {@link Control} object to a DOM element. + */ + class NgControl extends AbstractControlDirective { + + name: string; + + valueAccessor: ControlValueAccessor; + + validator: Function; + + path: List; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * Creates and binds a control group to a DOM element. + * + * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}. + * + * # Example + * + * In this example, we create the credentials and personal control groups. + * We can work with each group separately: check its validity, get its value, listen to its changes. + * + * ``` + * @Component({selector: "signup-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ *
+ * Login + * Password + *
+ *
Credentials are invalid
+ * + *
+ * Name + *
+ * + *
+ * `}) + * class SignupComp { + * onSignUp(value) { + * // value === {personal: {name: 'some name'}, + * // credentials: {login: 'some login', password: 'some password'}} + * } + * } + * + * ``` + */ + class NgControlGroup extends ControlContainer { + + onInit(): void; + + onDestroy(): void; + + control: ControlGroup; + + path: List; + + formDirective: Form; + } + + + /** + * Binds an existing control group to a DOM element. + * + * # Example + * + * In this example, we bind the control group to the form element, and we bind the login and + * password controls to the + * login and password elements. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "
" + + * "Login " + + * "Password " + + * "" + + * "
" + * }) + * class LoginComp { + * loginForm:ControlGroup; + * + * constructor() { + * this.loginForm = new ControlGroup({ + * login: new Control(""), + * password: new Control("") + * }); + * } + * + * onLogin() { + * // this.loginForm.value + * } + * } + * + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "
" + + * "Login " + + * "Password " + + * "" + + * "
" + * }) + * class LoginComp { + * credentials:{login:string, password:string} + * loginForm:ControlGroup; + * + * constructor() { + * this.loginForm = new ControlGroup({ + * login: new Control(""), + * password: new Control("") + * }); + * } + * + * onLogin() { + * // this.credentials.login === 'some login' + * // this.credentials.password === 'some password' + * } + * } + * ``` + */ + class NgFormModel extends ControlContainer implements Form { + + form: ControlGroup; + + directives: List; + + ngSubmit: void; + + onChange(_: any): void; + + formDirective: Form; + + control: ControlGroup; + + path: List; + + addControl(dir: NgControl): void; + + getControl(dir: NgControl): Control; + + removeControl(dir: NgControl): void; + + addControlGroup(dir: NgControlGroup): void; + + removeControlGroup(dir: NgControlGroup): void; + + getControlGroup(dir: NgControlGroup): ControlGroup; + + updateModel(dir: NgControl, value: any): void; + + onSubmit(): boolean; + } + + + /** + * Creates and binds a form object to a DOM element. + * + * # Example + * + * ``` + * @Component({selector: "signup-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ *
+ * Login + * Password + *
+ *
Credentials are invalid
+ * + *
+ * Name + *
+ * + *
+ * `}) + * class SignupComp { + * onSignUp(value) { + * // value === {personal: {name: 'some name'}, + * // credentials: {login: 'some login', password: 'some password'}} + * } + * } + * + * ``` + */ + class NgForm extends ControlContainer implements Form { + + form: ControlGroup; + + ngSubmit: void; + + formDirective: Form; + + control: ControlGroup; + + path: List; + + controls: StringMap; + + addControl(dir: NgControl): void; + + getControl(dir: NgControl): Control; + + removeControl(dir: NgControl): void; + + addControlGroup(dir: NgControlGroup): void; + + removeControlGroup(dir: NgControlGroup): void; + + getControlGroup(dir: NgControlGroup): ControlGroup; + + updateModel(dir: NgControl, value: any): void; + + onSubmit(): boolean; + } + + + /** + * A bridge between a control and a native element. + * + * Please see {@link DefaultValueAccessor} for more information. + */ + interface ControlValueAccessor { + + writeValue(obj: any): void; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The default accessor for writing a value and listening to changes that is used by the + * {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives. + * + * # Example + * ``` + * + * ``` + */ + class DefaultValueAccessor implements ControlValueAccessor { + + value: string; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The accessor for writing a value and listening to changes on a checkbox input element. + * + * # Example + * ``` + * + * ``` + */ + class CheckboxControlValueAccessor implements ControlValueAccessor { + + checked: boolean; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The accessor for writing a value and listening to changes on a select element. + */ + class SelectControlValueAccessor implements ControlValueAccessor { + + value: void; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * A list of all the form directives used as part of a `@View` annotation. + * + * This is a shorthand for importing them each individually. + */ + var formDirectives : List ; + + + /** + * Provides a set of validators used by form controls. + * + * # Example + * + * ``` + * var loginControl = new Control("", Validators.required) + * ``` + */ + class Validators { + } + + class NgValidator { + + validator: Function; + } + + class NgRequiredValidator extends NgValidator { + + validator: Function; + } + + + /** + * Creates a form object from a user-specified configuration. + * + * # Example + * + * ``` + * import {Component, View, bootstrap} from 'angular2/angular2'; + * import {FormBuilder, Validators, formDirectives, ControlGroup} from 'angular2/forms'; + * + * @Component({ + * selector: 'login-comp', + * viewInjector: [ + * FormBuilder + * ] + * }) + * @View({ + * template: ` + *
+ * Login + * + *
+ * Password + * Confirm password + *
+ *
+ * `, + * directives: [ + * formDirectives + * ] + * }) + * class LoginComp { + * loginForm: ControlGroup; + * + * constructor(builder: FormBuilder) { + * this.loginForm = builder.group({ + * login: ["", Validators.required], + * + * passwordRetry: builder.group({ + * password: ["", Validators.required], + * passwordConfirmation: ["", Validators.required] + * }) + * }); + * } + * } + * + * bootstrap(LoginComp) + * ``` + * + * This example creates a {@link ControlGroup} that consists of a `login` {@link Control}, and a + * nested + * {@link ControlGroup} that defines a `password` and a `passwordConfirmation` {@link Control}: + * + * ``` + * var loginForm = builder.group({ + * login: ["", Validators.required], + * + * passwordRetry: builder.group({ + * password: ["", Validators.required], + * passwordConfirmation: ["", Validators.required] + * }) + * }); + * + * ``` + */ + class FormBuilder { + + group(controlsConfig: StringMap, extra?: StringMap): ControlGroup; + + control(value: Object, validator?: Function): Control; + + array(controlsConfig: List, validator?: Function): ControlArray; + } + + var formInjectables : List ; + + + /** + * A dispatcher for all events happening in a view. + */ + interface EventDispatcher { + + + /** + * Called when an event was triggered for a on-* attribute on an element. + * @param {Map} locals Locals to be used to evaluate the + * event expressions + */ + dispatchEvent(elementIndex: number, eventName: string, locals: Map): void; + } + + class Renderer { + + + /** + * Creates a root host view that includes the given element. + * @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type + * ProtoViewDto.HOST_VIEW_TYPE + * @param {any} hostElementSelector css selector for the host element (will be queried against the + * main document) + * @return {RenderViewRef} the created view + */ + createRootHostView(hostProtoViewRef: RenderProtoViewRef, hostElementSelector: string): RenderViewRef; + + + /** + * Creates a regular view out of the given ProtoView + */ + createView(protoViewRef: RenderProtoViewRef): RenderViewRef; + + + /** + * Destroys the given view after it has been dehydrated and detached + */ + destroyView(viewRef: RenderViewRef): void; + + + /** + * Attaches a componentView into the given hostView at the given element + */ + attachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + + /** + * Detaches a componentView into the given hostView at the given element + */ + detachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + + /** + * Attaches a view into a ViewContainer (in the given parentView at the given element) at the + * given index. + */ + attachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + + /** + * Detaches a view into a ViewContainer (in the given parentView at the given element) at the + * given index. + */ + detachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + + /** + * Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views + * inside of the view pool. + */ + hydrateView(viewRef: RenderViewRef): void; + + + /** + * Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views + * inside of the view pool. + */ + dehydrateView(viewRef: RenderViewRef): void; + + + /** + * Returns the native element at the given location. + * Attention: In a WebWorker scenario, this should always return null! + */ + getNativeElementSync(location: RenderElementRef): any; + + + /** + * Sets a property on an element. + */ + setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void; + + + /** + * Sets an attribute on an element. + */ + setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string): void; + + + /** + * Sets a class on an element. + */ + setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void; + + + /** + * Sets a style on an element. + */ + setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void; + + + /** + * Calls a method on an element. + */ + invokeElementMethod(location: RenderElementRef, methodName: string, args: List): void; + + + /** + * Sets the value of a text node. + */ + setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void; + + + /** + * Sets the dispatcher for all events of the given view + */ + setEventDispatcher(viewRef: RenderViewRef, dispatcher: EventDispatcher): void; + } + + + /** + * Abstract reference to the element which can be marshaled across web-worker boundry. + * + * This interface is used by the {@link Renderer} api. + */ + interface RenderElementRef { + + + /** + * Reference to the {@link RenderViewRef} where the `RenderElementRef` is inside of. + */ + renderView: RenderViewRef; + + + /** + * Index of the element inside the {@link ViewRef}. + * + * This is used internally by the Angular framework to locate elements. + */ + boundElementIndex: number; + } + + class RenderViewRef { + } + + class RenderProtoViewRef { + } + + class DomRenderer extends Renderer { + + createRootHostView(hostProtoViewRef: RenderProtoViewRef, hostElementSelector: string): RenderViewRef; + + createView(protoViewRef: RenderProtoViewRef): RenderViewRef; + + destroyView(view: RenderViewRef): void; + + getNativeElementSync(location: RenderElementRef): any; + + attachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + setComponentViewRootNodes(componentViewRef: RenderViewRef, rootNodes: List): void; + + getRootNodes(viewRef: RenderViewRef): List; + + detachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + attachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + detachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + hydrateView(viewRef: RenderViewRef): void; + + dehydrateView(viewRef: RenderViewRef): void; + + setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void; + + setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string): void; + + setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void; + + setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void; + + invokeElementMethod(location: RenderElementRef, methodName: string, args: List): void; + + setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void; + + setEventDispatcher(viewRef: RenderViewRef, dispatcher: any): void; + } + + var DOCUMENT_TOKEN:any; + + + /** + * Declare reusable UI building blocks for an application. + * + * Each Angular component requires a single `@Component` and at least one `@View` annotation. The + * `@Component` + * annotation specifies when a component is instantiated, and which properties and hostListeners it + * binds to. + * + * When a component is instantiated, Angular + * - creates a shadow DOM for the component. + * - loads the selected template into the shadow DOM. + * - creates all the injectable objects configured with `hostInjector` and `viewInjector`. + * + * All template expressions and statements are then evaluated against the component instance. + * + * For details on the `@View` annotation, see {@link View}. + * + * ## Example + * + * ``` + * @Component({ + * selector: 'greet' + * }) + * @View({ + * template: 'Hello {{name}}!' + * }) + * class Greet { + * name: string; + * + * constructor() { + * this.name = 'World'; + * } + * } + * ``` + */ + class ComponentAnnotation extends DirectiveAnnotation { + + + /** + * Defines the used change detection strategy. + * + * When a component is instantiated, Angular creates a change detector, which is responsible for + * propagating + * the component's bindings. + * + * The `changeDetection` property defines, whether the change detection will be checked every time + * or only when the component + * tells it to do so. + */ + changeDetection: string; + + + /** + * Defines the set of injectable objects that are visible to its view dom children. + * + * ## Simple Example + * + * Here is an example of a class that can be injected: + * + * ``` + * class Greeter { + * greet(name:string) { + * return 'Hello ' + name + '!'; + * } + * } + * + * @Directive({ + * selector: 'needs-greeter' + * }) + * class NeedsGreeter { + * greeter:Greeter; + * + * constructor(greeter:Greeter) { + * this.greeter = greeter; + * } + * } + * + * @Component({ + * selector: 'greet', + * viewInjector: [ + * Greeter + * ] + * }) + * @View({ + * template: ``, + * directives: [NeedsGreeter] + * }) + * class HelloWorld { + * } + * + * ``` + */ + viewInjector: List; + } + + + /** + * Directives allow you to attach behavior to elements in the DOM. + * + * {@link Directive}s with an embedded view are called {@link Component}s. + * + * A directive consists of a single directive annotation and a controller class. When the + * directive's `selector` matches + * elements in the DOM, the following steps occur: + * + * 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor + * arguments. + * 2. Angular instantiates directives for each matched element using `ElementInjector` in a + * depth-first order, + * as declared in the HTML. + * + * ## Understanding How Injection Works + * + * There are three stages of injection resolution. + * - *Pre-existing Injectors*: + * - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if + * the dependency was + * specified as `@Optional`, returns `null`. + * - The platform injector resolves browser singleton resources, such as: cookies, title, + * location, and others. + * - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow + * the same parent-child hierarchy + * as the component instances in the DOM. + * - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each + * element has an `ElementInjector` + * which follow the same parent-child hierarchy as the DOM elements themselves. + * + * When a template is instantiated, it also must instantiate the corresponding directives in a + * depth-first order. The + * current `ElementInjector` resolves the constructor dependencies for each directive. + * + * Angular then resolves dependencies as follows, according to the order in which they appear in the + * {@link View}: + * + * 1. Dependencies on the current element + * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary + * 3. Dependencies on component injectors and their parents until it encounters the root component + * 4. Dependencies on pre-existing injectors + * + * + * The `ElementInjector` can inject other directives, element-specific special objects, or it can + * delegate to the parent + * injector. + * + * To inject other directives, declare the constructor parameter as: + * - `directive:DirectiveType`: a directive on the current element only + * - `@Ancestor() directive:DirectiveType`: any directive that matches the type between the current + * element and the + * Shadow DOM root. Current element is not included in the resolution, therefore even if it could + * resolve it, it will + * be ignored. + * - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent + * element only. + * - `@Query(DirectiveType) query:QueryList`: A live collection of direct child + * directives. + * - `@QueryDescendants(DirectiveType) query:QueryList`: A live collection of any + * child directives. + * + * To inject element-specific special objects, declare the constructor parameter as: + * - `element: ElementRef` to obtain a reference to logical element in the view. + * - `viewContainer: ViewContainerRef` to control child template instantiation, for + * {@link Directive} directives only + * - `bindingPropagation: BindingPropagation` to control change detection in a more granular way. + * + * ## Example + * + * The following example demonstrates how dependency injection resolves constructor arguments in + * practice. + * + * + * Assume this HTML template: + * + * ``` + *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ``` + * + * With the following `dependency` decorator and `SomeService` injectable class. + * + * ``` + * @Injectable() + * class SomeService { + * } + * + * @Directive({ + * selector: '[dependency]', + * properties: [ + * 'id: dependency' + * ] + * }) + * class Dependency { + * id:string; + * } + * ``` + * + * Let's step through the different ways in which `MyDirective` could be declared... + * + * + * ### No injection + * + * Here the constructor is declared with no arguments, therefore nothing is injected into + * `MyDirective`. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor() { + * } + * } + * ``` + * + * This directive would be instantiated with no dependencies. + * + * + * ### Component-level injection + * + * Directives can inject any injectable instance from the closest component injector or any of its + * parents. + * + * Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type + * from the parent + * component's injector. + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(someService: SomeService) { + * } + * } + * ``` + * + * This directive would be instantiated with a dependency on `SomeService`. + * + * + * ### Injecting a directive from the current element + * + * Directives can inject other directives declared on the current element. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(dependency: Dependency) { + * expect(dependency.id).toEqual(3); + * } + * } + * ``` + * This directive would be instantiated with `Dependency` declared at the same element, in this case + * `dependency="3"`. + * + * + * ### Injecting a directive from a direct parent element + * + * Directives can inject other directives declared on a direct parent element. By definition, a + * directive with a + * `@Parent` annotation does not attempt to resolve dependencies for the current element, even if + * this would satisfy + * the dependency. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Parent() dependency: Dependency) { + * expect(dependency.id).toEqual(2); + * } + * } + * ``` + * This directive would be instantiated with `Dependency` declared at the parent element, in this + * case `dependency="2"`. + * + * + * ### Injecting a directive from any ancestor elements + * + * Directives can inject other directives declared on any ancestor element (in the current Shadow + * DOM), i.e. on the + * parent element and its parents. By definition, a directive with an `@Ancestor` annotation does + * not attempt to + * resolve dependencies for the current element, even if this would satisfy the dependency. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Ancestor() dependency: Dependency) { + * expect(dependency.id).toEqual(2); + * } + * } + * ``` + * + * Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its + * parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would + * have returned + * `dependency="1"`. + * + * + * ### Injecting a live collection of direct child directives + * + * + * A directive can also query for other child directives. Since parent directives are instantiated + * before child directives, a directive can't simply inject the list of child directives. Instead, + * the directive injects a {@link QueryList}, which updates its contents as children are added, + * removed, or moved by a directive that uses a {@link ViewContainerRef} such as a `ng-for`, an + * `ng-if`, or an `ng-switch`. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Query(Dependency) dependencies:QueryList) { + * } + * } + * ``` + * + * This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and + * 6. Here, `Dependency` 5 would not be included, because it is not a direct child. + * + * ### Injecting a live collection of descendant directives + * + * By passing the descendant flag to `@Query` above, we can include the children of the child + * elements. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Query(Dependency, {descendants: true}) dependencies:QueryList) { + * } + * } + * ``` + * + * This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6. + * + * ### Optional injection + * + * The normal behavior of directives is to return an error when a specified dependency cannot be + * resolved. If you + * would like to inject `null` on unresolved dependency instead, you can annotate that dependency + * with `@Optional()`. + * This explicitly permits the author of a template to treat some of the surrounding directives as + * optional. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Optional() dependency:Dependency) { + * } + * } + * ``` + * + * This directive would be instantiated with a `Dependency` directive found on the current element. + * If none can be + * found, the injector supplies `null` instead of throwing an error. + * + * ## Example + * + * Here we use a decorator directive to simply define basic tool-tip behavior. + * + * ``` + * @Directive({ + * selector: '[tooltip]', + * properties: [ + * 'text: tooltip' + * ], + * hostListeners: { + * 'onmouseenter': 'onMouseEnter()', + * 'onmouseleave': 'onMouseLeave()' + * } + * }) + * class Tooltip{ + * text:string; + * overlay:Overlay; // NOT YET IMPLEMENTED + * overlayManager:OverlayManager; // NOT YET IMPLEMENTED + * + * constructor(overlayManager:OverlayManager) { + * this.overlay = overlay; + * } + * + * onMouseEnter() { + * // exact signature to be determined + * this.overlay = this.overlayManager.open(text, ...); + * } + * + * onMouseLeave() { + * this.overlay.close(); + * this.overlay = null; + * } + * } + * ``` + * In our HTML template, we can then add this behavior to a `
` or any other element with the + * `tooltip` selector, + * like so: + * + * ``` + *
+ * ``` + * + * Directives can also control the instantiation, destruction, and positioning of inline template + * elements: + * + * A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at + * runtime. + * The {@link ViewContainerRef} is created as a result of `