diff --git a/.gitignore b/.gitignore index b6d52bd9b9..91d27e6acf 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ www npm-debug*.log* *.plnkr.html plnkr.html +*.eplnkr.html +eplnkr.html *plnkr.no-link.html public/docs/*/latest/guide/cheatsheet.json protractor-results.txt diff --git a/gulpfile.js b/gulpfile.js index 8ca960a8dd..747931fa7a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -45,7 +45,8 @@ var STYLES_SOURCE_PATH = path.join(TOOLS_PATH, 'styles-builder/less'); var docShredder = require(path.resolve(TOOLS_PATH, 'doc-shredder/doc-shredder')); var exampleZipper = require(path.resolve(TOOLS_PATH, '_example-zipper/exampleZipper')); -var plunkerBuilder = require(path.resolve(TOOLS_PATH, 'plunker-builder/plunkerBuilder')); +var regularPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/regularPlunker')); +var embeddedPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/embeddedPlunker')); var fsUtils = require(path.resolve(TOOLS_PATH, 'fs-utils/fsUtils')); const isSilent = !!argv.silent; @@ -603,7 +604,8 @@ gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function() }); gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() { - return plunkerBuilder.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build }); + regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build }); + return embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build }); }); gulp.task('build-dart-cheatsheet', [], function() { diff --git a/public/_includes/_scripts-include.jade b/public/_includes/_scripts-include.jade index 9b4cd8048b..b9b2266f3e 100644 --- a/public/_includes/_scripts-include.jade +++ b/public/_includes/_scripts-include.jade @@ -64,4 +64,4 @@ script(src="//www.gstatic.com/feedback/api.js" type="text/javascript") script. - (function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}})(document,"script","twitter-wjs"); \ No newline at end of file + (function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}})(document,"script","twitter-wjs"); diff --git a/public/docs/ts/latest/guide/ngmodule.jade b/public/docs/ts/latest/guide/ngmodule.jade index 697cbe9c1a..fc8a0caaad 100644 --- a/public/docs/ts/latest/guide/ngmodule.jade +++ b/public/docs/ts/latest/guide/ngmodule.jade @@ -131,7 +131,7 @@ a#bootstrap :marked The samples in this chapter demonstrate the dynamic bootstrapping approach. - Try the live example. + Try the live example. ### Static bootstrapping with the Ahead-Of-Time (AOT) compiler @@ -606,7 +606,7 @@ a#feature-modules * No `ContactService` provider * No `HighlightDirective` conflict - Try the live example of version 2. + Try the live example. a#lazy-load .l-main-section @@ -782,7 +782,7 @@ a#hero-module The `CrisisModule` is much the same. There's nothing more to say that's new. - Try the live example. + Try the live example. a#shared-module .l-main-section diff --git a/public/resources/css/main.scss b/public/resources/css/main.scss index 0a0e019eab..f066ab6f72 100644 --- a/public/resources/css/main.scss +++ b/public/resources/css/main.scss @@ -55,6 +55,7 @@ @import 'module/features'; @import 'module/docs-landing'; @import 'module/copy'; +@import 'module/embedded-plunker'; /* * PRINT STYLES diff --git a/public/resources/css/module/_embedded-plunker.scss b/public/resources/css/module/_embedded-plunker.scss new file mode 100644 index 0000000000..0648c723be --- /dev/null +++ b/public/resources/css/module/_embedded-plunker.scss @@ -0,0 +1,3 @@ +live-example iframe { + height: 500px; +} diff --git a/public/resources/images/plunker/placeholder.png b/public/resources/images/plunker/placeholder.png new file mode 100644 index 0000000000..becea3bab3 Binary files /dev/null and b/public/resources/images/plunker/placeholder.png differ diff --git a/public/resources/js/directives/live-example.js b/public/resources/js/directives/live-example.js index 1946d02f43..dc300f69a1 100644 --- a/public/resources/js/directives/live-example.js +++ b/public/resources/js/directives/live-example.js @@ -5,7 +5,7 @@ * app this directive is contained in. * * Usage: -* text +* text * Example: *

Run Try the live example

. * // ~/resources/live-examples/{chapter}/ts/plnkr.html @@ -15,6 +15,12 @@ * *

Run

. * // ~/resources/live-examples/{chapter}/ts/minimal.plnkr.html +* +* +* // ~/resources/live-examples/{chapter}/ts/eplnkr.html +* +* +* // ~/resources/live-examples/{chapter}/ts/minimal.eplnkr.html */ angularIO.directive('liveExample', ['$location', function ($location) { @@ -26,42 +32,60 @@ angularIO.directive('liveExample', ['$location', function ($location) { function span(text) { return '' + text + ''; } + function embeddedTemplate(src, name) { + return '
' + + '' + + '
' + + 'plunker'; + } + return { restrict: 'E', - + scope: true, compile: function (tElement, attrs) { var text = tElement.text() || 'live example'; var ex = attrs.name || NgIoUtil.getExampleName($location); - var plnkr = ''; + var embedded = attrs.hasOwnProperty('embedded'); + var plnkr = embedded ? 'eplnkr' : 'plnkr'; var href, template; if (attrs.plnkr) { - plnkr = attrs.plnkr + '.'; + plnkr = attrs.plnkr + '.' + plnkr; } var isForDart = attrs.lang === 'dart' || NgIoUtil.isDoc($location, 'dart'); var isForJs = attrs.lang === 'js' || NgIoUtil.isDoc($location, 'js'); var exLang = isForDart ? 'dart' : isForJs ? 'js' : 'ts'; - var href = isForDart - ? 'http://angular-examples.github.io/' + ex - : '/resources/live-examples/' + ex + '/' + exLang + '/' + plnkr + 'plnkr.html'; - // Link to live example. - var template = a(text, { href: href, target: '_blank' }); + if (attrs.hasOwnProperty('embedded') && !isForDart) { + href = '/resources/live-examples/' + ex + '/' + exLang + '/' + plnkr + '.html' + template = embeddedTemplate(href, plnkr); + } else { + var href = isForDart + ? 'http://angular-examples.github.io/' + ex + : '/resources/live-examples/' + ex + '/' + exLang + '/' + plnkr + '.html' - // The hosted example and sources are in different locations for Dart. - // Also show link to sources for Dart, unless noSource is specified. - if (isForDart && !attrs.hasOwnProperty('nosource')) { - var srcText = attrs.srcText || 'view source'; - var srcHref = 'http://github.com/angular-examples/' + ex; - template = span(template + ' (' + a(srcText, { href: srcHref, target: '_blank' }) + ')'); + // Link to live example. + var template = a(text, { href: href, target: '_blank' }); + + // The hosted example and sources are in different locations for Dart. + // Also show link to sources for Dart, unless noSource is specified. + if (isForDart && !attrs.hasOwnProperty('nosource')) { + var srcText = attrs.srcText || 'view source'; + var srcHref = 'http://github.com/angular-examples/' + ex; + template = span(template + ' (' + a(srcText, { href: srcHref, target: '_blank' }) + ')'); + } } // UPDATE ELEMENT WITH NEW TEMPLATE tElement.html(template); // RETURN ELEMENT - return function (scope, element, attrs) { }; + return function (scope, element, attrs) { + scope.toggleEmbedded = function() { + scope.embeddedShow = !scope.embeddedShow; + } + }; } }; }]); diff --git a/tools/plunker-builder/builder.js b/tools/plunker-builder/builder.js new file mode 100644 index 0000000000..d8f910767d --- /dev/null +++ b/tools/plunker-builder/builder.js @@ -0,0 +1,320 @@ +'use strict'; + +// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes +var path = require('canonical-path'); +var Q = require('q'); +var _ = require('lodash'); +var jsdom = require("jsdom"); +var fs = require("fs"); +var globby = require('globby'); +var mkdirp = require('mkdirp'); + +var indexHtmlTranslator = require('./indexHtmlTranslator'); +var regionExtractor = require('../doc-shredder/regionExtractor'); + +class PlunkerBuilder { + constructor(basePath, destPath, options) { + this.basePath = basePath; + this.destPath = destPath; + this.options = options; + this.copyrights = {}; + + this._buildCopyrightStrings(); + } + + buildPlunkers() { + this._getPlunkerFiles(); + var errFn = this.options.errFn || function(e) { console.log(e); }; + var plunkerPaths = path.join(this.basePath, '**/*plnkr.json'); + var fileNames = globby.sync(plunkerPaths, { ignore: "**/node_modules/**"}); + fileNames.forEach((configFileName) => { + try { + this._buildPlunkerFrom(configFileName); + } catch (e) { + errFn(e); + } + }); + } + + _addPlunkerFiles(config, postData) { + this._addReadme(config, postData); + if (config.basePath.indexOf('/ts') > -1) { + // uses systemjs.config.js so add plunker version + this.options.addField(postData, 'systemjs.config.js', this.systemjsConfig); + this.options.addField(postData, 'tsconfig.json', this.tsconfig); + } + } + + _addReadme(config, postData) { + var existingFiles = config.fileNames.map(function(file) { + return file.substr(file.lastIndexOf('/') + 1); + }); + + if (existingFiles.indexOf('README.md') === -1) { + var plunkerReadme = this.readme + config.description; + this.options.addField(postData, 'README.md', plunkerReadme); + } + } + + _buildCopyrightStrings() { + var copyright = 'Copyright 2016 Google Inc. All Rights Reserved.\n' + + 'Use of this source code is governed by an MIT-style license that\n' + + 'can be found in the LICENSE file at http://angular.io/license'; + var pad = '\n\n'; + this.copyrights.jsCss = `${pad}/*\n${copyright}\n*/`; + this.copyrights.html = `${pad}`; + } + + // config has + // files: [] - optional array of globs - defaults to all js, ts, html, json, css and md files (with certain files removed) + // description: optional string - description of this plunker - defaults to the title in the index.html page. + // tags: [] - optional array of strings + // main: string - filename of what will become index.html in the plunker - defaults to index.html + _buildPlunkerFrom(configFileName) { + // replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name; + var outputFileName = `${this.options.plunkerFileName}.no-link.html`; + outputFileName = configFileName.substr(0, configFileName.length - 'plnkr.json'.length) + outputFileName; + var altFileName; + if (this.destPath && this.destPath.length > 0) { + var partPath = path.dirname(path.relative(this.basePath, outputFileName)); + var altFileName = path.join(this.destPath, partPath, path.basename(outputFileName)).replace('.no-link.', '.'); + } + try { + var config = this._initConfigAndCollectFileNames(configFileName); + var postData = this._createPostData(config); + this._addPlunkerFiles(config, postData); + var html = this._createPlunkerHtml(postData, altFileName); + if (this.options.writeNoLink) { + fs.writeFileSync(outputFileName, html, 'utf-8'); + } + if (altFileName) { + var altDirName = path.dirname(altFileName); + if (!fs.existsSync(altDirName)) { + mkdirp.sync(altDirName); + } + fs.writeFileSync(altFileName, html, 'utf-8'); + } + } catch (e) { + // if we fail delete the outputFile if it exists because it is an old one. + if (this._existsSync(outputFileName)) { + fs.unlinkSync(outputFileName); + } + if (altFileName && this._existsSync(altFileName)) { + fs.unlinkSync(altFileName); + } + throw e; + } + } + + _createBasePlunkerHtml(embedded, altFileName) { + // We extract the filename without extension + var targetName = ''; + if (altFileName) { + targetName = altFileName.split('/').pop().slice(0, -5); + } + var target = embedded ? targetName : '_self'; + var html = '' + html += `
` + + // html += '
' + // html += '
' + html += '' + html += ''; + return html; + } + + _createPostData(config) { + var postData = {}; + config.fileNames.forEach((fileName) => { + var content; + var extn = path.extname(fileName); + if (extn == '.png') { + content = this._encodeBase64(fileName); + fileName = fileName.substr(0, fileName.length - 4) + '.base64.png' + } else { + content = fs.readFileSync(fileName, 'utf-8'); + } + + if (extn == '.js' || extn == '.ts' || extn == '.css') { + content = content + this.copyrights.jsCss; + } else if (extn == '.html') { + content = content + this.copyrights.html; + } + // var escapedValue = escapeHtml(content); + + var relativeFileName = path.relative(config.basePath, fileName); + + if (relativeFileName == config.main) { + relativeFileName = 'index.html'; + } + + if (relativeFileName == 'index.html') { + content = indexHtmlTranslator.translate(content); + if (config.description == null) { + // set config.description to title from index.html + var matches = /(.*)<\/title>/.exec(content); + if (matches) { + config.description = matches[1]; + } + } + } + content = regionExtractor.removeDocTags(content, extn.substr(1)); + + this.options.addField(postData, relativeFileName, content); + }); + + var tags = ['angular2', 'example'].concat(config.tags || []); + tags.forEach(function(tag,ix) { + postData['tags[' + ix + ']'] = tag; + }); + + if (!this.options.embedded) { + postData.private = true; + + postData.description = "Angular 2 Example - " + config.description; + } else { + postData.title = "Angular 2 Example - " + config.description; + } + + // Embedded needs to add more content, so if the callback is available, we call it + if (this.options.extraData) { + this.options.extraData(postData, config); + } + return postData; + } + + _createPlunkerHtml(postData, altFileName) { + var baseHtml = this._createBasePlunkerHtml(this.options.embedded, altFileName); + var doc = jsdom.jsdom(baseHtml); + var form = doc.querySelector('form'); + _.forEach(postData, (value, key) => { + var ele = this._htmlToElement(doc, '<input type="hidden" name="' + key + '">'); + ele.setAttribute('value', value); + form.appendChild(ele) + }); + var html = doc.documentElement.outerHTML; + + return html; + } + + _encodeBase64(file) { + // read binary data + var bitmap = fs.readFileSync(file); + // convert binary data to base64 encoded string + return new Buffer(bitmap).toString('base64'); + } + + _existsSync(filename) { + try { + fs.accessSync(filename); + return true; + } catch(ex) { + return false; + } + } + + _getPlunkerFiles() { + // Assume plunker version is sibling of node_modules version + this.readme = fs.readFileSync(this.basePath + '/plunker.README.md', 'utf-8'); + var systemJsConfigPath = '/systemjs.config.plunker.js'; + if (this.options.build) { + systemJsConfigPath = '/systemjs.config.plunker.build.js'; + } + this.systemjsConfig = fs.readFileSync(this.basePath + systemJsConfigPath, 'utf-8'); + this.systemjsConfig += this.copyrights.jsCss; + this.tsconfig = fs.readFileSync(`${this.basePath}/tsconfig.json`, 'utf-8'); + } + + _htmlToElement(document, html) { + var div = document.createElement('div'); + div.innerHTML = html; + return div.firstChild; + } + + _initConfigAndCollectFileNames(configFileName) { + var basePath = path.dirname(configFileName); + var configSrc = fs.readFileSync(configFileName, 'utf-8'); + try { + var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {}; + } catch (e) { + throw new Error(`Plunker config - unable to parse json file: ${configFileName}\n${e}`); + } + + var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png']; + if (config.files) { + if (config.files.length > 0) { + if (config.files[0].substr(0, 1) == '!') { + config.files = defaultIncludes.concat(config.files); + } + } + } else { + config.files = defaultIncludes; + } + var gpaths = config.files.map(function(fileName) { + fileName = fileName.trim(); + if (fileName.substr(0,1) == '!') { + return "!" + path.join(basePath, fileName.substr(1)); + } else { + return path.join(basePath, fileName); + } + }); + + // var defaultExcludes = [ '!**/node_modules/**','!**/typings/**','!**/tsconfig.json', '!**/*plnkr.json', '!**/*plnkr.html', '!**/*plnkr.no-link.html' ]; + var defaultExcludes = [ + '!**/typings/**', + '!**/typings.json', + '!**/tsconfig.json', + '!**/*plnkr.*', + '!**/package.json', + '!**/example-config.json', + '!**/*.spec.*', + '!**/tslint.json', + '!**/.editorconfig', + '!**/systemjs.config.js', + '!**/wallaby.js', + '!**/karma-test-shim.js', + '!**/karma.conf.js', + '!**/spec.js' + ]; + Array.prototype.push.apply(gpaths, defaultExcludes); + + config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] }); + config.basePath = basePath; + + return config; + } +} + +module.exports = PlunkerBuilder; + +// not currently used. +// function escapeHtml(unsafe) { +// return unsafe +// .replace(/&/g, "&") +// .replace(/</g, "<") +// .replace(/>/g, ">") +// .replace(/"/g, """) +// .replace(/'/g, "'"); +// } + +//// Old version - no longer used +//function createPlunkerHtmlAsync(basePath, postData) { +// +// useNewWindow = false; +// jsdom.env({ +// html: createBasePlunkerHtml(useNewWindow), +// done: function (err, window) { +// var doc = window.document; +// var form = doc.querySelector('form'); +// +// _.forEach(postData, function(value, key) { +// var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">'); +// ele.setAttribute('value', value); +// form.appendChild(ele) +// }); +// var html = doc.documentElement.outerHTML; +// var outputFn = path.join(basePath, "plnkr.html"); +// fs.writeFileSync(outputFn, html, 'utf-8' ); +// } +// }); +//} diff --git a/tools/plunker-builder/embeddedPlunker.js b/tools/plunker-builder/embeddedPlunker.js new file mode 100644 index 0000000000..6da491702d --- /dev/null +++ b/tools/plunker-builder/embeddedPlunker.js @@ -0,0 +1,34 @@ +var PlunkerBuilder = require('./builder'); + +function buildPlunkers(basePath, destPath, options) { + configureBuilder(options); + var builder = new PlunkerBuilder(basePath, destPath, options); + builder.buildPlunkers(); +} + +function configureBuilder(options) { + options.addField = addField; + options.plunkerFileName = 'eplnkr'; + options.url = 'https://embed.plnkr.co?show=preview'; + options.writeNoLink = false; + options.embedded = true; + options.extraData = extraData; +} + +function extraData(postData, config) { + postData['source[type]'] = config.description || 'Angular 2 example'; + postData['source[url]'] = 'https://angular.io' +} + +function addField(postData, name, content) { + var encoding = 'utf8'; + if (name.split('.').pop === 'png') { + encoding = 'base64'; + } + postData[`entries[${name}][content]`] = content; + postData[`entries[${name}][encoding]`] = encoding; +} + +module.exports = { + buildPlunkers: buildPlunkers +}; diff --git a/tools/plunker-builder/plunkerBuilder.js b/tools/plunker-builder/plunkerBuilder.js deleted file mode 100644 index b2f1a4981d..0000000000 --- a/tools/plunker-builder/plunkerBuilder.js +++ /dev/null @@ -1,309 +0,0 @@ -// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes -var path = require('canonical-path'); -var Q = require('q'); -var _ = require('lodash'); -var jsdom = require("jsdom"); -var fs = require("fs"); -var globby = require('globby'); -var mkdirp = require('mkdirp'); - -var indexHtmlTranslator = require('./indexHtmlTranslator'); -var regionExtractor = require('../doc-shredder/regionExtractor'); -var COPYRIGHT, COPYRIGHT_JS_CSS, COPYRIGHT_HTML; -var README; // content of plunker.README.md for plunkers -var SYSTEMJS_CONFIG; // content of systemjs.config.js for plunkers that use systemjs -var TSCONFIG; // content of tsconfig.json for plunkers that use systemjs - -module.exports = { - buildPlunkers: buildPlunkers -}; - -buildCopyrightStrings(); - -function buildCopyrightStrings() { - var COPYRIGHT = 'Copyright 2016 Google Inc. All Rights Reserved.\n' - + 'Use of this source code is governed by an MIT-style license that\n' - + 'can be found in the LICENSE file at http://angular.io/license'; - var pad = '\n\n'; - COPYRIGHT_JS_CSS = pad + '/*\n' + COPYRIGHT + '\n*/'; - COPYRIGHT_HTML = pad + '<!-- \n' + COPYRIGHT + '\n-->' -} - -function buildPlunkers(basePath, destPath, options) { - getPlunkerFiles(basePath, options); - var errFn = options.errFn || function(e) { console.log(e); }; - var plunkerPaths = path.join(basePath, '**/*plnkr.json'); - var fileNames = globby.sync(plunkerPaths, { ignore: "**/node_modules/**"}); - fileNames.forEach(function(configFileName) { - try { - buildPlunkerFrom(configFileName, basePath, destPath); - } catch (e) { - errFn(e); - } - }); -} - -// config has -// files: [] - optional array of globs - defaults to all js, ts, html, json, css and md files (with certain files removed) -// description: optional string - description of this plunker - defaults to the title in the index.html page. -// tags: [] - optional array of strings -// main: string - filename of what will become index.html in the plunker - defaults to index.html -function buildPlunkerFrom(configFileName, basePath, destPath) { - // replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name; - var outputFileName = configFileName.substr(0, configFileName.length - 'plnkr.json'.length) + 'plnkr.no-link.html'; - var altFileName; - if (destPath && destPath.length > 0) { - var partPath = path.dirname(path.relative(basePath, outputFileName)); - var altFileName = path.join(destPath, partPath, path.basename(outputFileName)).replace('.no-link.', '.'); - } - try { - var config = initConfigAndCollectFileNames(configFileName); - var postData = createPostData(config); - addPlunkerFiles(config, postData); - var html = createPlunkerHtml(postData); - fs.writeFileSync(outputFileName, html, 'utf-8'); - if (altFileName) { - var altDirName = path.dirname(altFileName); - if (!fs.existsSync(altDirName)) { - mkdirp.sync(altDirName); - } - fs.writeFileSync(altFileName, html, 'utf-8'); - } - } catch (e) { - // if we fail delete the outputFile if it exists because it is an old one. - if (existsSync(outputFileName)) { - fs.unlinkSync(outputFileName); - } - if (altFileName && existsSync(altFileName)) { - fs.unlinkSync(altFileName); - } - throw e; - } -} - -function addPlunkerFiles(config, postData) { - addReadme(config, postData); - if (config.basePath.indexOf('/ts') > -1) { - // uses systemjs.config.js so add plunker version - postData['files[systemjs.config.js]'] = SYSTEMJS_CONFIG; - postData['files[tsconfig.json]'] = TSCONFIG; - } -} - -function addReadme(config, postData) { - var existingFiles = config.fileNames.map(function(file) { - return file.substr(file.lastIndexOf('/') + 1); - }); - - if (existingFiles.indexOf('README.md') === -1) { - var plunkerReadme = README + config.description; - postData['files[README.md]'] = plunkerReadme; - } -} - -function getPlunkerFiles(basePath, options) { - // Assume plunker version is sibling of node_modules version - README = fs.readFileSync(basePath + '/plunker.README.md', 'utf-8'); - var systemJsConfigPath = '/systemjs.config.plunker.js'; - if (options.build) { - systemJsConfigPath = '/systemjs.config.plunker.build.js'; - } - SYSTEMJS_CONFIG = fs.readFileSync(basePath + systemJsConfigPath, 'utf-8'); - SYSTEMJS_CONFIG += COPYRIGHT_JS_CSS; - TSCONFIG = fs.readFileSync(basePath + '/tsconfig.json', 'utf-8'); -} - -function initConfigAndCollectFileNames(configFileName) { - var basePath = path.dirname(configFileName); - var configSrc = fs.readFileSync(configFileName, 'utf-8'); - try { - var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {}; - } catch (e) { - throw new Error("Plunker config - unable to parse json file: " + configFileName + '\n ' + e); - } - - var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png']; - if (config.files) { - if (config.files.length > 0) { - if (config.files[0].substr(0, 1) == '!') { - config.files = defaultIncludes.concat(config.files); - } - } - } else { - config.files = defaultIncludes; - } - var gpaths = config.files.map(function(fileName) { - fileName = fileName.trim(); - if (fileName.substr(0,1) == '!') { - return "!" + path.join(basePath, fileName.substr(1)); - } else { - return path.join(basePath, fileName); - } - }); - - // var defaultExcludes = [ '!**/node_modules/**','!**/typings/**','!**/tsconfig.json', '!**/*plnkr.json', '!**/*plnkr.html', '!**/*plnkr.no-link.html' ]; - var defaultExcludes = [ - '!**/typings/**', - '!**/typings.json', - '!**/tsconfig.json', - '!**/*plnkr.*', - '!**/package.json', - '!**/example-config.json', - '!**/*.spec.*', - '!**/tslint.json', - '!**/.editorconfig', - '!**/systemjs.config.js', - '!**/wallaby.js', - '!**/karma-test-shim.js', - '!**/karma.conf.js', - '!**/spec.js' - ]; - Array.prototype.push.apply(gpaths, defaultExcludes); - - config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] }); - config.basePath = basePath; - - return config; -} - -function createPostData(config) { - var postData = {}; - config.fileNames.forEach(function(fileName) { - var content; - var extn = path.extname(fileName); - if (extn == '.png') { - content = encodeBase64(fileName); - fileName = fileName.substr(0, fileName.length - 4) + '.base64.png' - } else { - content = fs.readFileSync(fileName, 'utf-8'); - } - - if (extn == '.js' || extn == '.ts' || extn == '.css') { - content = content + COPYRIGHT_JS_CSS; - } else if (extn == '.html') { - content = content + COPYRIGHT_HTML; - } - // var escapedValue = escapeHtml(content); - - var relativeFileName = path.relative(config.basePath, fileName); - - if (relativeFileName == config.main) { - relativeFileName = 'index.html'; - } - - if (relativeFileName == 'index.html') { - content = indexHtmlTranslator.translate(content); - if (config.description == null) { - // set config.description to title from index.html - var matches = /<title>(.*)<\/title>/.exec(content); - if (matches) { - config.description = matches[1]; - } - } - } - content = regionExtractor.removeDocTags(content, extn.substr(1)); - - postData['files[' + relativeFileName + ']'] = content; - }); - - // Leave here in case we want to add a md file later. - // postData['files[license.md]'] = fs.readFileSync(path.join(__dirname, "license.md")); - - var tags = ['angular2', 'example'].concat(config.tags || []); - tags.forEach(function(tag,ix) { - postData['tags[' + ix + ']'] = tag; - }); - // postData['tags[0]'] = "angular2"; - // postData['tags[1]'] = "example"; - postData.private = true; - - postData.description = "Angular 2 Example - " + config.description; - return postData; -} - -function existsSync(filename) { - try { - fs.accessSync(filename); - return true; - } catch(ex) { - return false; - } -} - -function encodeBase64(file) { - // read binary data - var bitmap = fs.readFileSync(file); - // convert binary data to base64 encoded string - return new Buffer(bitmap).toString('base64'); -} - -function createPlunkerHtml(postData) { - var baseHtml = createBasePlunkerHtml(false); - var doc = jsdom.jsdom(baseHtml); - var form = doc.querySelector('form'); - _.forEach(postData, function(value, key) { - var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">'); - ele.setAttribute('value', value); - form.appendChild(ele) - }); - var html = doc.documentElement.outerHTML; - - return html; -} - - -function createBasePlunkerHtml(useNewWindow) { - var url = 'http://plnkr.co/edit/?p=preview'; - // If the form posts to target="_blank", pop-up blockers can cause it not to work. - // If a user choses to bypass pop-up blocker one time and click the link, they will arrive at - // a new default plnkr, not a plnkr with the desired template. Given this undesired behavior, - // some may still want to open the plnk in a new window by opting-in via ctrl+click. The - // newWindow param allows for this possibility. - var target = useNewWindow ? '_blank' : '_self'; - var html = '<!DOCTYPE html><html lang="en"><body>' - html += '<form id="mainForm" method="post" action="' + url + '" target="' + target + '">' - - // html += '<div class="button"><button id="formButton" type="submit">Create Plunker</button></div>' - // html += '</form><script>document.getElementById("formButton").click();</script>' - html += '</form><script>document.getElementById("mainForm").submit();</script>' - html += '</body></html>'; - return html; -} - -function htmlToElement(document, html) { - var div = document.createElement('div'); - div.innerHTML = html; - return div.firstChild; -} - -// not currently used. -function escapeHtml(unsafe) { - return unsafe - .replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -} - -//// Old version - no longer used -//function createPlunkerHtmlAsync(basePath, postData) { -// -// useNewWindow = false; -// jsdom.env({ -// html: createBasePlunkerHtml(useNewWindow), -// done: function (err, window) { -// var doc = window.document; -// var form = doc.querySelector('form'); -// -// _.forEach(postData, function(value, key) { -// var ele = htmlToElement(doc, '<input type="hidden" name="' + key + '">'); -// ele.setAttribute('value', value); -// form.appendChild(ele) -// }); -// var html = doc.documentElement.outerHTML; -// var outputFn = path.join(basePath, "plnkr.html"); -// fs.writeFileSync(outputFn, html, 'utf-8' ); -// } -// }); -//} diff --git a/tools/plunker-builder/regularPlunker.js b/tools/plunker-builder/regularPlunker.js new file mode 100644 index 0000000000..7dda2fa7c0 --- /dev/null +++ b/tools/plunker-builder/regularPlunker.js @@ -0,0 +1,23 @@ +var PlunkerBuilder = require('./builder'); + +function buildPlunkers(basePath, destPath, options) { + configureBuilder(options); + var builder = new PlunkerBuilder(basePath, destPath, options); + builder.buildPlunkers(); +} + +function configureBuilder(options) { + options.addField = addField; + options.plunkerFileName = 'plnkr'; + options.url = 'http://plnkr.co/edit/?p=preview'; + options.writeNoLink = true; + options.embedded = false; +} + +function addField(postData, name, content) { + postData[`files[${name}]`] = content; +} + +module.exports = { + buildPlunkers: buildPlunkers +};