feat(aio): add code-example and code-tabs

* move embedded components to EmbeddedModule
* add PrettyPrint service; load pretty print js dynamically
* make code-example to syntax highlighting w/ `prettyPrintOne`
* add code-tabs
* Implement copy code button
This commit is contained in:
Ward Bell 2017-03-26 13:32:29 -07:00 committed by Pete Bacon Darwin
parent 2e4fe7fd2e
commit 837ed788f4
19 changed files with 877 additions and 144 deletions

View File

@ -34,8 +34,7 @@ describe('site App', function() {
it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', () => {
page.navigateTo('guide/component-styles');
const codeExample = element.all(by.css('code-example')).first();
expect(page.getInnerHtml(codeExample))
.toContain('@Component({\n selector: \'hero-app\',\n template: `\n &lt;h1&gt;Tour of Heroes&lt;/h1&gt;');
expect(page.getInnerHtml(codeExample)).toContain('&lt;h1&gt;Tour of Heroes&lt;/h1&gt;');
});
describe('api-docs', () => {

View File

@ -1,6 +1,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
@ -9,6 +10,7 @@ import { MdButtonModule} from '@angular/material/button';
import { MdIconModule} from '@angular/material/icon';
import { MdInputModule } from '@angular/material/input';
import { MdSidenavModule } from '@angular/material/sidenav';
import { MdTabsModule } from '@angular/material';
import { Platform } from '@angular/material/core';
// Temporary fix for MdSidenavModule issue:
@ -18,7 +20,7 @@ import 'rxjs/add/operator/first';
import { AppComponent } from 'app/app.component';
import { ApiService } from 'app/embedded/api/api.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { embeddedComponents, EmbeddedComponents } from 'app/embedded';
import { EmbeddedModule } from 'app/embedded/embedded.module';
import { GaService } from 'app/shared/ga.service';
import { Logger } from 'app/shared/logger.service';
import { LocationService } from 'app/shared/location.service';
@ -35,16 +37,18 @@ import { AutoScrollService } from 'app/shared/auto-scroll.service';
@NgModule({
imports: [
BrowserModule,
EmbeddedModule,
HttpModule,
BrowserAnimationsModule,
MdButtonModule,
MdIconModule,
MdInputModule,
MdToolbarModule,
MdSidenavModule
MdSidenavModule,
MdTabsModule
],
declarations: [
AppComponent,
embeddedComponents,
DocViewerComponent,
TopMenuComponent,
NavMenuComponent,
@ -54,7 +58,6 @@ import { AutoScrollService } from 'app/shared/auto-scroll.service';
],
providers: [
ApiService,
EmbeddedComponents,
GaService,
Logger,
Location,
@ -66,7 +69,6 @@ import { AutoScrollService } from 'app/shared/auto-scroll.service';
Platform,
AutoScrollService,
],
entryComponents: [ embeddedComponents ],
bootstrap: [AppComponent]
})
export class AppModule {

View File

@ -1,28 +0,0 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { CodeExampleComponent } from './code-example.component';
describe('CodeExampleComponent', () => {
let component: CodeExampleComponent;
let fixture: ComponentFixture<CodeExampleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CodeExampleComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CodeExampleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,73 +0,0 @@
/* tslint:disable component-selector */
import { Component, OnInit, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
// TODO(i): add clipboard copy functionality
/**
* Angular.io Code Example
*
* Pretty renders a code block, primarily used in the docs and API reference. Can be used within an Angular app, or
* independently, provided that it is dynamically generated by the component resolver.
*
* Usage:
* <code-example [language]="..." [escape]="..." [format]="..." [showcase]="..." [animated]="...">
* console.log('Hello World')
* </code-example>
*/
@Component({
selector: 'code-example',
template: '<pre class="{{classes}}"><code class="{{animatedClasses}}" #codeContainer></code></pre>'
})
export class CodeExampleComponent implements OnInit, AfterViewInit {
@ViewChild('codeContainer') codeContainerRef: ElementRef;
language: string; // could be javascript, dart, typescript
// TODO(i): escape doesn't seem to be currently supported in the original code
escape: string; // could be 'html'
format: string; // some css class
showcase: string; // a string with the value 'true'
animated = false;
// TODO(i): could we use @HostBinding instead or does the CSS have to be scoped to <pre> and <code>
classes: string;
animatedClasses: string;
constructor(private elementRef: ElementRef) {
// TODO(i): @Input should be supported for host elements and should just do a one off initialization of properties
// from the host element => talk to Tobias
['language', 'escape', 'format', 'showcase', 'animated'].forEach(inputName => {
if (!this[inputName]) {
this[inputName] = this.elementRef.nativeElement.getAttribute(inputName);
}
});
}
ngOnInit() {
const showcaseClass = this.showcase === 'true' ? ' is-showcase' : '';
this.classes = `
prettyprint
${this.format ? this.format : ''}
${this.language ? 'lang-' + this.language : '' }
${showcaseClass ? showcaseClass : ''}
`.trim();
this.animatedClasses = `${this.animated ? 'animated fadeIn' : ''}`;
// Security: the codeExampleContent is the original innerHTML of the host element provided by
// docs authors and as such its considered to be safe for innerHTML purposes
this.codeContainerRef.nativeElement.innerHTML = this.elementRef.nativeElement.codeExampleContent;
}
ngAfterViewInit() {
// TODO(i): import prettify.js from this file so that we don't need to preload it via index.html
// whenever a code example is used, use syntax highlighting.
// if(prettyPrint) {
// prettyPrint();
// }
}
}

View File

@ -0,0 +1,94 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Component, DebugElement, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { CodeExampleComponent } from './code-example.component';
describe('CodeExampleComponent', () => {
let hostComponent: HostComponent;
let codeComponent: TestCodeComponent;
let codeExampleDe: DebugElement;
let codeExampleComponent: CodeExampleComponent;
let fixture: ComponentFixture<HostComponent>;
const oneLineCode = `const foo = "bar";`;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ CodeExampleComponent, HostComponent, TestCodeComponent ],
});
});
function createComponent(codeExampleContent = '') {
fixture = TestBed.createComponent(HostComponent);
hostComponent = fixture.componentInstance;
codeExampleDe = fixture.debugElement.children[0];
codeExampleComponent = codeExampleDe.componentInstance;
codeComponent = codeExampleDe.query(By.directive(TestCodeComponent)).componentInstance;
// Copy the CodeExample's innerHTML (content)
// into the `codeExampleContent` property as the DocViewer does
codeExampleDe.nativeElement.codeExampleContent = codeExampleContent;
fixture.detectChanges();
}
it('should create CodeExampleComponent', () => {
createComponent();
expect(codeExampleComponent).toBeTruthy('CodeExampleComponent');
});
it('should pass content to CodeComponent (<aio-code>)', () => {
createComponent(oneLineCode);
expect(codeComponent.code).toBe(oneLineCode);
});
it('should pass language to CodeComponent', () => {
TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example language="html"></code-example>'}});
createComponent(oneLineCode);
expect(codeComponent.language).toBe('html');
});
it('should pass linenums to CodeComponent', () => {
TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example linenums="true"></code-example>'}});
createComponent(oneLineCode);
expect(codeComponent.linenums).toBe('true');
});
it('should add title (header) when set `title` attribute', () => {
TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example title="Great Example"></code-example>'}});
createComponent(oneLineCode);
const actual = codeExampleDe.query(By.css('header')).nativeElement.innerText;
expect(actual).toBe('Great Example');
});
});
//// Test helpers ////
// tslint:disable:member-ordering
@Component({
selector: 'aio-code',
template: `
<div>lang: {{language}}</div>
<div>linenums: {{linenums}}</div>
code: <pre>{{someCode}}</pre>
`
})
class TestCodeComponent {
@Input() code = '';
@Input() language: string;
@Input() linenums: boolean | number;
get someCode() {
return this.code && this.code.length > 30 ? this.code.substr(0, 30) + '...' : this.code;
}
}
@Component({
selector: 'aio-host-comp',
template: `<code-example></code-example>`
})
class HostComponent { }

View File

@ -0,0 +1,41 @@
/* tslint:disable component-selector */
import { Component, ElementRef, OnInit } from '@angular/core';
/**
* An embeddable code block that displays nicely formatted code.
* Example usage:
*
* ```
* <code-example language="ts" linenums="2" class="special" title="Do Stuff">
* // a code block
* console.log('do stuff');
* </code-example>
* ```
*/
@Component({
selector: 'code-example',
template: `
<header *ngIf="title">{{title}}</header>
<aio-code [code]="code" [language]="language" [linenums]="linenums"></aio-code>
`
})
export class CodeExampleComponent implements OnInit { // implements AfterViewInit {
code: string;
language: string;
linenums: boolean | number;
title: string;
constructor(private elementRef: ElementRef) {
const element = this.elementRef.nativeElement;
this.language = element.getAttribute('language') || '';
this.linenums = element.getAttribute('linenums');
this.title = element.getAttribute('title') || '';
}
ngOnInit() {
// The `codeExampleContent` property is set by the DocViewer when it builds this component.
// It is the original innerHTML of the host element.
this.code = this.elementRef.nativeElement.codeExampleContent;
}
}

View File

@ -0,0 +1,72 @@
/* tslint:disable component-selector */
import { Component, ElementRef, OnInit } from '@angular/core';
export interface TabInfo {
title: string;
language: string;
code: string;
}
/**
* An embedded component used to generate tabbed code panes inside docs
*
* The innerHTML of the `<code-tabs>` component should contain `<code-pane>` elements.
* Each `<code-pane>` has the same interface as the embedded `<code-example>` component.
* The optional `linenums` attribute is the default `linenums` for each code pane.
*/
@Component({
selector: 'code-tabs',
template: `
<md-tab-group>
<md-tab *ngFor="let tab of tabs">
<template md-tab-label>
<span class="{{tab.class}}">{{ tab.title }}</span>
</template>
<aio-code [code]="tab.code" [language]="tab.language" [linenums]="tab.linenums" class="{{ tab.class }}"></aio-code>
</md-tab>
</md-tab-group>
`
})
export class CodeTabsComponent implements OnInit {
tabs: TabInfo[];
linenumsDefault: string;
constructor(private elementRef: ElementRef) { }
ngOnInit() {
const element = this.elementRef.nativeElement;
this.linenumsDefault = this.getLinenums(element);
// The `codeTabsContent` property is set by the DocViewer when it builds this component.
// It is the original innerHTML of the host element.
const content = element.codeTabsContent;
this.processContent(content);
}
processContent(content: string) {
// We add it to an element so that we can easily parse the HTML
const element = document.createElement('div');
// **Security:** `codeTabsContent` is provided by docs authors and as such its considered to
// be safe for innerHTML purposes.
element.innerHTML = content;
this.tabs = [];
const codeExamples = element.querySelectorAll('code-pane');
for (let i = 0; i < codeExamples.length; i++) {
const codeExample = codeExamples.item(i);
const tab = {
code: codeExample.innerHTML,
class: codeExample.getAttribute('class'),
language: codeExample.getAttribute('language'),
linenums: this.getLinenums(codeExample),
title: codeExample.getAttribute('title')
};
this.tabs.push(tab);
}
}
getLinenums(element: Element) {
const linenums = element.getAttribute('linenums');
return linenums == null ? this.linenumsDefault : linenums;
}
}

View File

@ -0,0 +1,146 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Component, DebugElement } from '@angular/core';
import { CodeComponent } from './code.component';
import { CopierService } from 'app/shared//copier.service';
import { Logger } from 'app/shared/logger.service';
import { PrettyPrinter } from './pretty-printer.service';
const oneLineCode = 'const foo = "bar";';
const multiLineCode = `
&lt;hero-details&gt;
&lt;h2&gt;Bah Dah Bing&lt;/h2&gt;
&lt;hero-team&gt;
&lt;h3&gt;NYC Team&lt;/h3&gt;
&lt;/hero-team&gt;
&lt;/hero-details&gt;`;
describe('CodeComponent', () => {
let codeComponentDe: DebugElement;
let codeComponent: CodeComponent;
let hostComponent: HostComponent;
let fixture: ComponentFixture<HostComponent>;
// WARNING: Chance of cross-test pollution
// CodeComponent injects PrettyPrintService
// Once PrettyPrintService runs once _anywhere_, its ctor loads `prettify.js`
// which sets `window['prettyPrintOne']`
// That global survives these tests unless
// we take strict measures to wipe it out in the `afterAll`
// and make sure THAT runs after the tests by making component creation async
afterAll(() => {
delete window['prettyPrint'];
delete window['prettyPrintOne'];
});
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CodeComponent, HostComponent ],
providers: [
PrettyPrinter,
{provide: CopierService, useClass: TestCopierService },
{provide: Logger, useClass: TestLogger }
]
})
.compileComponents();
}));
// Must be async because
// CodeComponent creates PrettyPrintService which async loads `prettify.js`.
// If not async, `afterAll` finishes before tests do!
beforeEach(async(() => {
fixture = TestBed.createComponent(HostComponent);
hostComponent = fixture.componentInstance;
codeComponentDe = fixture.debugElement.children[0];
codeComponent = codeComponentDe.componentInstance;
fixture.detectChanges();
}));
it('should create CodeComponent', () => {
expect(codeComponentDe.name).toBe('aio-code', 'selector');
expect(codeComponent).toBeTruthy('CodeComponent');
});
it('should format a one-line code sample', () => {
// 'pln' spans are a tell-tale for syntax highlighing
const spans = codeComponentDe.nativeElement.querySelectorAll('span.pln');
expect(spans.length).toBeGreaterThan(0, 'formatted spans');
});
it('should format a one-line code sample without linenums by default', () => {
// `<li>`s are a tell-tale for line numbers
const lis = codeComponentDe.nativeElement.querySelectorAll('li');
expect(lis.length).toBe(0, 'should be no linenums');
});
it('should add line numbers to one-line code sample when linenums set true', () => {
hostComponent.linenums = 'true';
fixture.detectChanges();
// `<li>`s are a tell-tale for line numbers
const lis = codeComponentDe.nativeElement.querySelectorAll('li');
expect(lis.length).toBe(1, 'has linenums');
});
it('should format multi-line code with linenums by default', () => {
hostComponent.code = multiLineCode;
fixture.detectChanges();
// `<li>`s are a tell-tale for line numbers
const lis = codeComponentDe.nativeElement.querySelectorAll('li');
expect(lis.length).toBeGreaterThan(0, 'has linenums');
});
it('should not format multi-line code when linenums set false', () => {
hostComponent.linenums = false;
hostComponent.code = multiLineCode;
fixture.detectChanges();
// `<li>`s are a tell-tale for line numbers
const lis = codeComponentDe.nativeElement.querySelectorAll('li');
expect(lis.length).toBe(0, 'should be no linenums');
});
it('should call copier service when copy button clicked', () => {
const copierService: TestCopierService = <any> codeComponentDe.injector.get(CopierService) ;
const button = fixture.debugElement.query(By.css('button')).nativeElement;
expect(copierService.copyText.calls.count()).toBe(0, 'before click');
button.click();
expect(copierService.copyText.calls.count()).toBe(1, 'after click');
});
it('should copy code text when copy button clicked', () => {
const copierService: TestCopierService = <any> codeComponentDe.injector.get(CopierService) ;
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(copierService.copyText.calls.argsFor(0)[0]).toEqual(oneLineCode, 'after click');
});
});
//// Test helpers ////
// tslint:disable:member-ordering
@Component({
selector: 'aio-host-comp',
template: `
<aio-code [code]="code" [language]="language" [linenums]="linenums"></aio-code>
`
})
class HostComponent {
code = oneLineCode;
language: string;
linenums: boolean | number | string;
}
class TestCopierService {
copyText = jasmine.createSpy('copyText');
}
class TestLogger {
log = jasmine.createSpy('log');
error = jasmine.createSpy('error');
}

View File

@ -0,0 +1,114 @@
import { Component, ElementRef, ViewChild, OnChanges, OnDestroy, Input } from '@angular/core';
import { Logger } from 'app/shared/logger.service';
import { PrettyPrinter } from './pretty-printer.service';
import { CopierService } from 'app/shared/copier.service';
const originalLabel = 'Copy Code';
const copiedLabel = 'Copied!';
/**
* Formatted Code Block
*
* Pretty renders a code block, used in the docs and API reference by the code-example and
* code-tabs embedded components.
* It includes a "copy" button that will send the content to the clipboard when clicked
*
* Example usage:
*
* ```
* <aio-code [code]="variableContainingCode" [language]="ts" [linenums]="true"></aio-code>
* ```
*
*/
@Component({
selector: 'aio-code',
template: `
<button class="copy-button" #copyButton (click)="doCopy()">{{ buttonLabel }}</button>
<pre class="prettyprint lang-{{language}}">
<code class="animated fadeIn" #codeContainer></code>
</pre>
`
})
export class CodeComponent implements OnChanges {
/**
* The language of the code to render
* (could be javascript, dart, typescript, etc)
*/
@Input()
language: string;
/**
* Whether to display line numbers:
* - false: don't display
* - true: do display
* - number: do display but start at the given number
*/
@Input()
linenums: boolean | number | string;
/**
* The code to be formatted, this should already be HTML encoded
*/
@Input()
code: string;
/**
* The label to show on the copy button
*/
buttonLabel = originalLabel;
/**
* The element in the template that will display the formatted code
*/
@ViewChild('codeContainer') codeContainer: ElementRef;
constructor(
private pretty: PrettyPrinter,
private copier: CopierService,
private logger: Logger) {}
ngOnChanges() {
if (!this.code) { return; }
const linenums = this.getLinenums();
this.setCodeHtml(this.code); // start with unformatted code
this.pretty.formatCode(this.code, this.language, linenums).subscribe(
formattedCode => this.setCodeHtml(formattedCode),
err => { /* ignore failure to format */ }
);
}
private setCodeHtml(formattedCode: string) {
// **Security:** `codeExampleContent` is provided by docs authors and as such its considered to
// be safe for innerHTML purposes.
this.codeContainer.nativeElement.innerHTML = formattedCode;
}
doCopy() {
// We take the innerText because we don't want it to be HTML encoded
const code = this.codeContainer.nativeElement.innerText;
if (this.copier.copyText(code)) {
this.logger.log('Copied code to clipboard:', code);
// change the button label (for one second)
this.buttonLabel = copiedLabel;
setTimeout(() => this.buttonLabel = originalLabel, 1000);
} else {
this.logger.error('ERROR copying code to clipboard:', code);
}
}
getLinenums() {
const linenums =
typeof this.linenums === 'boolean' ? this.linenums :
this.linenums === 'true' ? true :
this.linenums === 'false' ? false :
typeof this.linenums === 'string' ? parseInt(this.linenums, 10) :
this.linenums;
// if no linenums, enable line numbers if more than one line
return linenums == null || linenums === NaN ?
(this.code.match(/\n/g) || []).length > 1 : linenums;
}
}

View File

@ -0,0 +1,63 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { fromPromise } from 'rxjs/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';
import { Logger } from 'app/shared/logger.service';
declare const System;
type PrettyPrintOne = (code: string, language?: string, linenums?: number | boolean) => string;
/**
* Wrapper around the prettify.js library
*/
@Injectable()
export class PrettyPrinter {
private prettyPrintOne: Observable<PrettyPrintOne>;
constructor(private logger: Logger) {
this.prettyPrintOne = fromPromise(this.getPrettyPrintOne()).share();
}
private getPrettyPrintOne(): Promise<PrettyPrintOne> {
const ppo = window['prettyPrintOne'];
return ppo ? Promise.resolve(ppo) :
// prettify.js is not in window global; load it with webpack loader
System.import('assets/js/prettify.js')
.then(
() => window['prettyPrintOne'],
err => {
const msg = 'Cannot get prettify.js from server';
this.logger.error(msg, err);
// return a pretty print fn that always fails.
return () => { throw new Error(msg); };
});
}
/**
* Format code snippet as HTML
* @param {string} code - the code snippet to format; should already be HTML encoded
* @param {string} [language] - The language of the code to render (could be javascript, html, typescript, etc)
* @param {string|number} [linenums] - Whether to display line numbers:
* - false: don't display
* - true: do display
* - number: do display but start at the given number
* @returns Observable<string> - Observable of formatted code
*/
formatCode(code: string, language?: string, linenums?: number | boolean) {
return this.prettyPrintOne.map(ppo => {
try {
return ppo(code, language, linenums);
} catch (err) {
const msg = `Could not format code that begins '${code.substr(0, 50)}...'.`;
console.error(msg, err);
throw new Error(msg);
}
})
.first(); // complete immediately
}
}

View File

@ -0,0 +1,46 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PrettyPrinter } from './code/pretty-printer.service';
import { CopierService } from 'app/shared/copier.service';
// Any components that we want to use inside embedded components must be declared or imported here
// It is not enough just to import them inside the AppModule
// Reusable components (used inside embedded components)
import { MdTabsModule } from '@angular/material';
import { CodeComponent } from './code/code.component';
// Embedded Components
import { ApiListComponent } from './api/api-list.component';
import { CodeExampleComponent } from './code/code-example.component';
import { CodeTabsComponent } from './code/code-tabs.component';
import { DocTitleComponent } from './doc-title.component';
/** Components that can be embedded in docs
* such as CodeExampleComponent, LiveExampleComponent,...
*/
export const embeddedComponents: any[] = [
ApiListComponent, CodeExampleComponent, DocTitleComponent, CodeTabsComponent
];
/** Injectable class w/ property returning components that can be embedded in docs */
export class EmbeddedComponents {
components = embeddedComponents;
}
@NgModule({
imports: [ CommonModule, MdTabsModule ],
declarations: [
embeddedComponents,
CodeComponent
],
providers: [
EmbeddedComponents,
PrettyPrinter,
CopierService
],
entryComponents: [ embeddedComponents ]
})
export class EmbeddedModule { }

View File

@ -1,13 +0,0 @@
import { ApiListComponent } from './api/api-list.component';
import { CodeExampleComponent } from './code-example.component';
import { DocTitleComponent } from './doc-title.component';
/** Components that can be embedded in docs such as CodeExampleComponent, LiveExampleComponent,... */
export const embeddedComponents: any[] = [
ApiListComponent, CodeExampleComponent, DocTitleComponent
];
/** Injectable class w/ property returning components that can be embedded in docs */
export class EmbeddedComponents {
components = embeddedComponents;
}

View File

@ -3,7 +3,7 @@ import { ComponentFactoryResolver, ElementRef, Injector, NgModule, OnInit, ViewC
import { By } from '@angular/platform-browser';
import { DocViewerComponent } from './doc-viewer.component';
import { DocumentContents } from 'app/documents/document.service';
import { embeddedComponents, EmbeddedComponents } from 'app/embedded';
import { EmbeddedModule, embeddedComponents, EmbeddedComponents } from 'app/embedded/embedded.module';
/// Embedded Test Components ///
@ -65,9 +65,10 @@ class BazComponent implements OnInit {
}
///// Test Module //////
const embeddedTestComponents = [FooComponent, BarComponent, BazComponent, ...embeddedComponents];
const embeddedTestComponents = [FooComponent, BarComponent, BazComponent];
@NgModule({
imports: [ EmbeddedModule ],
entryComponents: embeddedTestComponents
})
class TestModule { }

View File

@ -4,7 +4,7 @@ import {
Output, ViewEncapsulation
} from '@angular/core';
import { EmbeddedComponents } from 'app/embedded';
import { EmbeddedComponents } from 'app/embedded/embedded.module';
import { DocumentContents } from 'app/documents/document.service';
interface EmbeddedComponentFactory {

View File

@ -0,0 +1,66 @@
/**
* This class is based on the code in the following projects:
*
* - https://github.com/zenorocha/select
* - https://github.com/zenorocha/clipboard.js/
*
* Both released under MIT license - © Zeno Rocha
*/
export class CopierService {
private fakeElem: HTMLTextAreaElement;
/**
* Creates a fake textarea element, sets its value from `text` property,
* and makes a selection on it.
*/
createFake(text: string) {
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
// Create a fake element to hold the contents to copy
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
// Move element to the same position vertically
const yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = text;
document.body.appendChild(this.fakeElem);
this.fakeElem.select();
this.fakeElem.setSelectionRange(0, this.fakeElem.value.length);
}
removeFake() {
if (this.fakeElem) {
document.body.removeChild(this.fakeElem);
this.fakeElem = null;
}
}
copyText(text: string) {
try {
this.createFake(text);
return document.execCommand('copy');
} catch (err) {
return false;
} finally {
this.removeFake();
}
}
}

46
aio/src/assets/js/prettify.js Executable file
View File

@ -0,0 +1,46 @@
!function(){/*
Copyright (C) 2006 Google Inc.
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.
*/
window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function T(a){function d(e){var b=e.charCodeAt(0);if(92!==b)return b;var a=e.charAt(1);return(b=w[a])?b:"0"<=a&&"7">=a?parseInt(e.substring(1),8):"u"===a||"x"===a?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[0-9A-Fa-f]{4}|\\x[0-9A-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\s\S]|-|[^-\\]/g);e=
[];var a="^"===b[0],c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;a<g;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),k;a+2<g&&"-"===b[a+1]?(k=d(b[a+2]),a+=2):k=h;e.push([h,k]);65>k||122<h||(65>k||90<h||e.push([Math.max(65,h)|32,Math.min(k,90)|32]),97>k||122<h||e.push([Math.max(97,h)&-33,Math.min(k,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];g=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=g[1]+1?g[1]=Math.max(g[1],h[1]):b.push(g=h);for(a=0;a<b.length;++a)h=b[a],
c.push(f(h[0])),h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(f(h[1])));c.push("]");return c.join("")}function v(e){for(var a=e.source.match(/(?:\[(?:[^\x5C\x5D]|\\[\s\S])*\]|\\u[A-Fa-f0-9]{4}|\\x[A-Fa-f0-9]{2}|\\[0-9]+|\\[^ux0-9]|\(\?[:!=]|[\(\)\^]|[^\x5B\x5C\(\)\^]+)/g),c=a.length,d=[],g=0,h=0;g<c;++g){var k=a[g];"("===k?++h:"\\"===k.charAt(0)&&(k=+k.substring(1))&&(k<=h?d[k]=-1:a[g]=f(k))}for(g=1;g<d.length;++g)-1===d[g]&&(d[g]=++A);for(h=g=0;g<c;++g)k=a[g],"("===k?(++h,d[h]||(a[g]="(?:")):"\\"===
k.charAt(0)&&(k=+k.substring(1))&&k<=h&&(a[g]="\\"+d[k]);for(g=0;g<c;++g)"^"===a[g]&&"^"!==a[g+1]&&(a[g]="");if(e.ignoreCase&&n)for(g=0;g<c;++g)k=a[g],e=k.charAt(0),2<=k.length&&"["===e?a[g]=b(k):"\\"!==e&&(a[g]=k.replace(/[a-zA-Z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var A=0,n=!1,l=!1,m=0,c=a.length;m<c;++m){var p=a[m];if(p.ignoreCase)l=!0;else if(/[a-z]/i.test(p.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){n=!0;
l=!1;break}}for(var w={b:8,t:9,n:10,v:11,f:12,r:13},r=[],m=0,c=a.length;m<c;++m){p=a[m];if(p.global||p.multiline)throw Error(""+p);r.push("(?:"+v(p)+")")}return new RegExp(r.join("|"),l?"gi":"g")}function U(a,d){function f(a){var c=a.nodeType;if(1==c){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)f(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)v[l]="\n",n[l<<1]=A++,n[l++<<1|1]=a}}else if(3==c||4==c)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[ \t\r\n]+/g,
" "),v[l]=c,n[l<<1]=A,A+=c.length,n[l++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,v=[],A=0,n=[],l=0;f(a);return{a:v.join("").replace(/\n$/,""),c:n}}function J(a,d,f,b,v){f&&(a={h:a,l:1,j:null,m:null,a:f,c:null,i:d,g:null},b(a),v.push.apply(v,a.g))}function V(a){for(var d=void 0,f=a.firstChild;f;f=f.nextSibling)var b=f.nodeType,d=1===b?d?a:f:3===b?W.test(f.nodeValue)?a:d:d;return d===a?void 0:d}function G(a,d){function f(a){for(var l=a.i,m=a.h,c=[l,"pln"],p=0,w=a.a.match(v)||[],r={},e=0,t=w.length;e<
t;++e){var z=w[e],q=r[z],g=void 0,h;if("string"===typeof q)h=!1;else{var k=b[z.charAt(0)];if(k)g=z.match(k[1]),q=k[0];else{for(h=0;h<A;++h)if(k=d[h],g=z.match(k[1])){q=k[0];break}g||(q="pln")}!(h=5<=q.length&&"lang-"===q.substring(0,5))||g&&"string"===typeof g[1]||(h=!1,q="src");h||(r[z]=q)}k=p;p+=z.length;if(h){h=g[1];var B=z.indexOf(h),D=B+h.length;g[2]&&(D=z.length-g[2].length,B=D-h.length);q=q.substring(5);J(m,l+k,z.substring(0,B),f,c);J(m,l+k+B,h,K(q,h),c);J(m,l+k+D,z.substring(D),f,c)}else c.push(l+
k,q)}a.g=c}var b={},v;(function(){for(var f=a.concat(d),l=[],m={},c=0,p=f.length;c<p;++c){var w=f[c],r=w[3];if(r)for(var e=r.length;0<=--e;)b[r.charAt(e)]=w;w=w[1];r=""+w;m.hasOwnProperty(r)||(l.push(w),m[r]=null)}l.push(/[\0-\uffff]/);v=T(l)})();var A=d.length;return f}function y(a){var d=[],f=[];a.tripleQuotedStrings?d.push(["str",/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
null,"'\""]):a.multiLineStrings?d.push(["str",/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):d.push(["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);a.verbatimStrings&&f.push(["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);var b=a.hashComments;b&&(a.cStyleComments?(1<b?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
null,"#"]),f.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null]));if(b=a.regexLiterals){var v=(b=1<b?"":"\n\r")?".":"[\\S\\s]";f.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+v+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+v+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&f.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&f.push(["kwd",new RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,
null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(b),null]);return G(d,f)}function L(a,d,f){function b(a){var c=a.nodeType;if(1==c&&!A.test(a.className))if("br"===a.nodeName)v(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((3==c||4==c)&&f){var d=a.nodeValue,q=d.match(n);q&&(c=d.substring(0,q.index),a.nodeValue=c,(d=d.substring(q.index+q[0].length))&&
a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),v(a),c||a.parentNode.removeChild(a))}}function v(a){function b(a,c){var d=c?a.cloneNode(!1):a,k=a.parentNode;if(k){var k=b(k,1),e=a.nextSibling;k.appendChild(d);for(var f=e;f;f=e)e=f.nextSibling,k.appendChild(f)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=b(a.nextSibling,0);for(var d;(d=a.parentNode)&&1===d.nodeType;)a=d;c.push(a)}for(var A=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,l=a.ownerDocument,m=l.createElement("li");a.firstChild;)m.appendChild(a.firstChild);
for(var c=[m],p=0;p<c.length;++p)b(c[p]);d===(d|0)&&c[0].setAttribute("value",d);var w=l.createElement("ol");w.className="linenums";d=Math.max(0,d-1|0)||0;for(var p=0,r=c.length;p<r;++p)m=c[p],m.className="L"+(p+d)%10,m.firstChild||m.appendChild(l.createTextNode("\u00a0")),w.appendChild(m);a.appendChild(w)}function t(a,d){for(var f=d.length;0<=--f;){var b=d[f];I.hasOwnProperty(b)?E.console&&console.warn("cannot override language handler %s",b):I[b]=a}}function K(a,d){a&&I.hasOwnProperty(a)||(a=/^\s*</.test(d)?
"default-markup":"default-code");return I[a]}function M(a){var d=a.j;try{var f=U(a.h,a.l),b=f.a;a.a=b;a.c=f.c;a.i=0;K(d,b)(a);var v=/\bMSIE\s(\d+)/.exec(navigator.userAgent),v=v&&8>=+v[1],d=/\n/g,A=a.a,n=A.length,f=0,l=a.c,m=l.length,b=0,c=a.g,p=c.length,w=0;c[p]=n;var r,e;for(e=r=0;e<p;)c[e]!==c[e+2]?(c[r++]=c[e++],c[r++]=c[e++]):e+=2;p=r;for(e=r=0;e<p;){for(var t=c[e],z=c[e+1],q=e+2;q+2<=p&&c[q+1]===z;)q+=2;c[r++]=t;c[r++]=z;e=q}c.length=r;var g=a.h;a="";g&&(a=g.style.display,g.style.display="none");
try{for(;b<m;){var h=l[b+2]||n,k=c[w+2]||n,q=Math.min(h,k),B=l[b+1],D;if(1!==B.nodeType&&(D=A.substring(f,q))){v&&(D=D.replace(d,"\r"));B.nodeValue=D;var N=B.ownerDocument,u=N.createElement("span");u.className=c[w+1];var y=B.parentNode;y.replaceChild(u,B);u.appendChild(B);f<h&&(l[b+1]=B=N.createTextNode(A.substring(q,h)),y.insertBefore(B,u.nextSibling))}f=q;f>=h&&(b+=2);f>=k&&(w+=2)}}finally{g&&(g.style.display=a)}}catch(x){E.console&&console.log(x&&x.stack||x)}}var E=window,C=["break,continue,do,else,for,if,return,while"],
F=[[C,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],H=[F,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
O=[F,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],P=[F,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"],
F=[F,"abstract,async,await,constructor,debugger,enum,eval,export,function,get,implements,instanceof,interface,let,null,set,undefined,var,with,yield,Infinity,NaN"],Q=[C,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],R=[C,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],C=[C,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],
S=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,W=/\S/,X=y({keywords:[H,P,O,F,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",Q,R,C],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),I={};t(X,["default-code"]);t(G([],[["pln",/^[^<?]+/],["dec",
/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));t(G([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,
"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);t(G([],[["atv",/^[\s\S]+/]]),["uq.val"]);t(y({keywords:H,
hashComments:!0,cStyleComments:!0,types:S}),"c cc cpp cxx cyc m".split(" "));t(y({keywords:"null,true,false"}),["json"]);t(y({keywords:P,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:S}),["cs"]);t(y({keywords:O,cStyleComments:!0}),["java"]);t(y({keywords:C,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);t(y({keywords:Q,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);t(y({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",
hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);t(y({keywords:R,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);t(y({keywords:F,cStyleComments:!0,regexLiterals:!0}),["javascript","js","ts","typescript"]);t(y({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,
regexLiterals:!0}),["coffee"]);t(G([],[["str",/^[\s\S]+/]]),["regex"]);var Y=E.PR={createSimpleLexer:G,registerLangHandler:t,sourceDecorator:y,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:E.prettyPrintOne=function(a,d,f){f=f||!1;d=d||null;var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";
b=b.firstChild;f&&L(b,f,!0);M({j:d,m:f,h:b,l:1,a:null,i:null,c:null,g:null});return b.innerHTML},prettyPrint:E.prettyPrint=function(a,d){function f(){for(var b=E.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;p<t.length&&c.now()<b;p++){for(var d=t[p],l=g,m=d;m=m.previousSibling;){var n=m.nodeType,u=(7===n||8===n)&&m.nodeValue;if(u?!/^\??prettify\b/.test(u):3!==n||/\S/.test(m.nodeValue))break;if(u){l={};u.replace(/\b(\w+)=([\w:.%+-]+)/g,function(a,b,c){l[b]=c});break}}m=d.className;if((l!==g||r.test(m))&&
!e.test(m)){n=!1;for(u=d.parentNode;u;u=u.parentNode)if(q.test(u.tagName)&&u.className&&r.test(u.className)){n=!0;break}if(!n){d.className+=" prettyprinted";n=l.lang;if(!n){var n=m.match(w),C;!n&&(C=V(d))&&z.test(C.tagName)&&(n=C.className.match(w));n&&(n=n[1])}if(y.test(d.tagName))u=1;else var u=d.currentStyle,x=v.defaultView,u=(u=u?u.whiteSpace:x&&x.getComputedStyle?x.getComputedStyle(d,null).getPropertyValue("white-space"):0)&&"pre"===u.substring(0,3);x=l.linenums;(x="true"===x||+x)||(x=(x=m.match(/\blinenums\b(?::(\d+))?/))?
x[1]&&x[1].length?+x[1]:!0:!1);x&&L(d,x,u);M({j:n,h:d,m:x,l:u,a:null,i:null,c:null,g:null})}}}p<t.length?E.setTimeout(f,250):"function"===typeof a&&a()}for(var b=d||document.body,v=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],t=[],n=0;n<b.length;++n)for(var l=0,m=b[n].length;l<m;++l)t.push(b[n][l]);var b=null,c=Date;c.now||(c={now:function(){return+new Date}});var p=0,w=/\blang(?:uage)?-([\w.]+)(?!\S)/,r=/\bprettyprint\b/,
e=/\bprettyprinted\b/,y=/pre|xmp/i,z=/^code$/i,q=/^(?:pre|code|xmp)$/i,g={};f()}},H=E.define;"function"===typeof H&&H.amd&&H("google-code-prettify",[],function(){return Y})})();}()

View File

@ -1,24 +1,180 @@
code-example code {
@include codeblock($backgroundgray);
code-example,
code-tabs md-tab-body {
background-color: $backgroundgray;
border: 0.5px solid $lightgray;
border-radius: 5px;
color: $darkgray;
width: 100%;
}
code-example[language=bash] code {
width: 100%;
}
.prettyprint {
display: block;
padding: 10px;
margin: 24px auto;
}
code-example header {
background-color: $mediumgray;
border: 0.5px solid $mediumgray;
border-radius: 5px 5px 0 0;
color: $offwhite;
font-size: 16px;
padding: 10px;
margin: -10px;
}
code-example.is-anti-pattern header {
border: 2px solid $anti-pattern;
background: $anti-pattern;
}
code-example.is-anti-pattern,
code-tabs.is-anti-pattern md-tab-body {
border: 0.5px solid $anti-pattern;
}
aio-code pre {
display: flex;
}
line-height: 1.5;
.prettyprint.lang-bash code {
@include codeblock($darkgray);
color: $codegreen;
}
.code-example pre, code-example pre {
margin: 0px;
padding: 0px;
white-space: pre-wrap;
}
li {
line-height: 60%;
}
}
$blue-grey-50: #ECEFF1;
$blue-grey-100: #CFD8DC;
$blue-grey-200: #B0BEC5;
$blue-grey-300: #90A4AE;
$blue-grey-400: #78909C;
$blue-grey-500: #607D8B;
$blue-grey-600: #546E7A;
$blue-grey-700: #455A64;
$blue-grey-800: #37474F;
$blue-grey-900: #263238;
$pink-50: #FCE4EC;
$pink-100: #F8BBD0;
$pink-200: #F48FB1;
$pink-300: #F06292;
$pink-400: #EC407A;
$pink-500: #E91E63;
$pink-600: #D81B60;
$pink-700: #C2185B;
$pink-800: #AD1457;
$pink-900: #880E4F;
$pink-A100: #FF80AB;
$pink-A200: #FF4081;
$pink-A400: #F50057;
$pink-A700: #C51162;
$teal-50: #E0F2F1;
$teal-100: #B2DFDB;
$teal-200: #80CBC4;
$teal-300: #4DB6AC;
$teal-400: #26A69A;
$teal-500: #009688;
$teal-600: #00897B;
$teal-700: #00796B;
$teal-800: #00695C;
$teal-900: #004D40;
$teal-A100: #A7FFEB;
$teal-A200: #64FFDA;
$teal-A400: #1DE9B6;
$teal-A700: #00BFA5;
$white: #FFFFFF;
/*
* Screen Colors
*
*/
.pnk,
.blk {
border-radius: 4px;
padding: 2px 4px;
}
.pnk {
background: $blue-grey-50;
color: $blue-grey-900;
}
.blk {
background: $blue-grey-900;
}
.otl {
outline: 1px solid rgba($blue-grey-700, .56);
}
.kwd {
color: $pink-600;
}
.typ,
.tag {
color: $pink-600;
}
.str,
.atv {
color: $teal-700;
}
.atn {
color: $teal-700;
}
.com {
color: $teal-700;
}
.lit {
color: $teal-700;
}
.pun {
color: $blue-grey-700;
}
.pln {
color: $blue-grey-700;
}
.dec {
color: $teal-700;
}
/*
* Print Colors
*
*/
@media print {
border: none;
box-shadow: none;
ol {
background: $white;
}
.kwd {
color: $pink-600;
}
.typ,
.tag {
color: $pink-600;
}
.str,
.atv {
color: $teal-700;
}
.atn {
color: $teal-700;
}
.com {
color: $teal-700;
}
.lit {
color: $teal-700;
}
.pun {
color: $blue-grey-700;
}
.pln {
color: $blue-grey-700;
}
.dec {
color: $teal-700;
}
}

View File

@ -17,7 +17,8 @@ $darkgray: #333;
$black: #0A1014;
$codegreen: #17ff0b;
$orange: #FF9800;
$anti-pattern: $brightred;
// GRADIENTS
$bluegradient: linear-gradient(145deg,#0D47A1,#42A5F5);
$redgradient: linear-gradient(145deg,$darkred,$brightred);
$redgradient: linear-gradient(145deg,$darkred,$brightred);

View File

@ -1 +1 @@
{$ doc.contents | escape $}
{$ doc.contents | escape $}