Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fountain.ts

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fountain.ts - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

readme.md

6

dist/fountain.d.ts

@@ -11,8 +11,8 @@ import { Token } from './token';

export declare class Fountain {
private title;
private tokens;
private title_page;
private html;
private scanner;
private inlineLex;
constructor();
parse(script: string, getTokens?: boolean): Script;
to_html(token: Token): string;
}

@@ -7,110 +7,54 @@ "use strict";

function Fountain() {
this.title_page = [];
this.html = [];
this.scanner = new scanner_1.Scanner;
this.inlineLex = new lexer_1.InlineLexer;
}
Fountain.prototype.parse = function (script, getTokens) {
this.tokens = new scanner_1.Scanner().tokenize(script);
var token, i = this.tokens.length;
while (i--) {
token = this.tokens[i];
token.text = new lexer_1.InlineLexer().reconstruct(token.text);
switch (token.type) {
case 'title':
this.title_page.push('<h1>' + token.text + '</h1>');
this.title = token.text.replace('<br />', ' ').replace(/<(?:.|\n)*?>/g, '');
break;
case 'credit':
this.title_page.push('<p class=\"credit\">' + token.text + '</p>');
break;
case 'author':
this.title_page.push('<p class=\"authors\">' + token.text + '</p>');
break;
case 'authors':
this.title_page.push('<p class=\"authors\">' + token.text + '</p>');
break;
case 'source':
this.title_page.push('<p class=\"source\">' + token.text + '</p>');
break;
case 'notes':
this.title_page.push('<p class=\"notes\">' + token.text + '</p>');
break;
case 'draft_date':
this.title_page.push('<p class=\"draft-date\">' + token.text + '</p>');
break;
case 'date':
this.title_page.push('<p class=\"date\">' + token.text + '</p>');
break;
case 'contact':
this.title_page.push('<p class=\"contact\">' + token.text + '</p>');
break;
case 'copyright':
this.title_page.push('<p class=\"copyright\">' + token.text + '</p>');
break;
case 'scene_heading':
this.html.push('<h3' + (token.scene_number ? ' id=\"' + token.scene_number + '\">' : '>') + token.text + '</h3>');
break;
case 'transition':
this.html.push('<h2>' + token.text + '</h2>');
break;
case 'dual_dialogue_begin':
this.html.push('<div class=\"dual-dialogue\">');
break;
case 'dialogue_begin':
this.html.push('<div class=\"dialogue' + (token.dual ? ' ' + token.dual : '') + '\">');
break;
case 'character':
this.html.push('<h4>' + token.text + '</h4>');
break;
case 'parenthetical':
this.html.push('<p class=\"parenthetical\">' + token.text + '</p>');
break;
case 'dialogue':
this.html.push('<p>' + token.text + '</p>');
break;
case 'dialogue_end':
this.html.push('</div>');
break;
case 'dual_dialogue_end':
this.html.push('</div>');
break;
case 'section':
this.html.push('<p class=\"section\" data-depth=\"' + token.depth + '\">' + token.text + '</p>');
break;
case 'synopsis':
this.html.push('<p class=\"synopsis\">' + token.text + '</p>');
break;
case 'note':
this.html.push('<!-- ' + token.text + '-->');
break;
case 'boneyard_begin':
this.html.push('<!-- ');
break;
case 'boneyard_end':
this.html.push(' -->');
break;
case 'action':
this.html.push('<p>' + token.text + '</p>');
break;
case 'centered':
this.html.push('<p class=\"centered\">' + token.text + '</p>');
break;
case 'page_break':
this.html.push('<hr />');
break;
case 'line_break':
this.html.push('<br />');
break;
}
}
var _this = this;
this.tokens = this.scanner.tokenize(script);
var title = this.tokens.find(function (token) { return token.type === 'title'; });
return {
title: this.title,
title: title ? this.inlineLex.reconstruct(title.text)
.replace('<br />', ' ').replace(/<(?:.|\n)*?>/g, '') : undefined,
html: {
title_page: this.title_page.join(''),
script: this.html.join('')
title_page: this.tokens.filter(function (token) { return token.is_title; }).map(function (token) { return _this.to_html(token); }).join(''),
script: this.tokens.filter(function (token) { return !token.is_title; }).map(function (token) { return _this.to_html(token); }).join('')
},
tokens: getTokens ? this.tokens.reverse() : undefined
tokens: getTokens ? this.tokens : undefined
};
};
Fountain.prototype.to_html = function (token) {
token.text = this.inlineLex.reconstruct(token.text);
switch (token.type) {
case 'title': return '<h1>' + token.text + '</h1>';
case 'credit': return '<p class=\"credit\">' + token.text + '</p>';
case 'author': return '<p class=\"authors\">' + token.text + '</p>';
case 'authors': return '<p class=\"authors\">' + token.text + '</p>';
case 'source': return '<p class=\"source\">' + token.text + '</p>';
case 'notes': return '<p class=\"notes\">' + token.text + '</p>';
case 'draft_date': return '<p class=\"draft-date\">' + token.text + '</p>';
case 'date': return '<p class=\"date\">' + token.text + '</p>';
case 'contact': return '<p class=\"contact\">' + token.text + '</p>';
case 'copyright': return '<p class=\"copyright\">' + token.text + '</p>';
case 'scene_heading': return '<h3' + (token.scene_number ? ' id=\"' + token.scene_number + '\">' : '>') + token.text + '</h3>';
case 'transition': return '<h2>' + token.text + '</h2>';
case 'dual_dialogue_begin': return '<div class=\"dual-dialogue\">';
case 'dialogue_begin': return '<div class=\"dialogue' + (token.dual ? ' ' + token.dual : '') + '\">';
case 'character': return '<h4>' + token.text + '</h4>';
case 'parenthetical': return '<p class=\"parenthetical\">' + token.text + '</p>';
case 'dialogue': return '<p>' + token.text + '</p>';
case 'dialogue_end': return '</div>';
case 'dual_dialogue_end': return '</div>';
case 'section': return '<p class=\"section\" data-depth=\"' + token.depth + '\">' + token.text + '</p>';
case 'synopsis': return '<p class=\"synopsis\">' + token.text + '</p>';
case 'note': return '<!-- ' + token.text + ' -->';
case 'boneyard_begin': return '<!-- ';
case 'boneyard_end': return ' -->';
case 'action': return '<p>' + token.text + '</p>';
case 'centered': return '<p class=\"centered\">' + token.text + '</p>';
case 'page_break': return '<hr />';
case 'line_break': return '<br />';
}
};
return Fountain;
}());
exports.Fountain = Fountain;

@@ -6,3 +6,3 @@ export declare class Lexer {

private inline;
reconstruct(text: string): string;
reconstruct(line: string): string;
}

@@ -46,18 +46,16 @@ "use strict";

}
InlineLexer.prototype.reconstruct = function (text) {
if (!text) {
InlineLexer.prototype.reconstruct = function (line) {
if (!line)
return;
}
var styles = ['underline', 'italic', 'bold', 'bold_italic', 'italic_underline', 'bold_underline', 'bold_italic_underline'];
var i = styles.length;
var style, match;
text = text.replace(regex_1.regex.note_inline, this.inline.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, this.inline.line_break);
while (i--) {
style = styles[i];
var match;
var styles = ['bold_italic_underline', 'bold_underline', 'italic_underline', 'bold_italic', 'bold', 'italic', 'underline'];
line = line.replace(regex_1.regex.note_inline, this.inline.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, this.inline.line_break);
for (var _i = 0, styles_1 = styles; _i < styles_1.length; _i++) {
var style = styles_1[_i];
match = regex_1.regex[style];
if (match.test(text)) {
text = text.replace(match, this.inline[style]);
if (match.test(line)) {
line = line.replace(match, this.inline[style]);
}
}
return text.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
return line.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
};

@@ -64,0 +62,0 @@ return InlineLexer;

import { Token } from './token';
export declare class Scanner {
private tokens;
constructor();
tokenize(script: string): Token[];
}

@@ -10,12 +10,14 @@ "use strict";

Scanner.prototype.tokenize = function (script) {
var src = new lexer_1.Lexer().reconstruct(script).split(regex_1.regex.splitter), line, match, parts, text, meta, x, xlen, dual;
var i = src.length;
while (i--) {
line = src[i];
// reverse the array so that dual dialog can be constructed bottom up
var source = new lexer_1.Lexer().reconstruct(script).split(regex_1.regex.splitter).reverse();
var line, match, dual;
for (var _i = 0, source_1 = source; _i < source_1.length; _i++) {
line = source_1[_i];
/** title page */
if (regex_1.regex.title_page.test(line)) {
match = line.replace(regex_1.regex.title_page, '\n$1').split(regex_1.regex.splitter).reverse();
for (x = 0, xlen = match.length; x < xlen; x++) {
parts = match[x].replace(regex_1.regex.cleaner, '').split(/\:\n*/);
this.tokens.push({ type: parts[0].trim().toLowerCase().replace(' ', '_'), text: parts[1].trim() });
for (var _a = 0, match_1 = match; _a < match_1.length; _a++) {
var item = match_1[_a];
var pair = item.replace(regex_1.regex.cleaner, '').split(/\:\n*/);
this.tokens.push({ type: pair[0].trim().toLowerCase().replace(' ', '_'), is_title: true, text: pair[1].trim() });
}

@@ -26,3 +28,3 @@ continue;

if (match = line.match(regex_1.regex.scene_heading)) {
text = match[1] || match[2];
var text = match[1] || match[2], meta = void 0;
if (text.indexOf(' ') !== text.length - 2) {

@@ -50,3 +52,3 @@ if (meta = text.match(regex_1.regex.scene_number)) {

if (match[1].indexOf(' ') !== match[1].length - 2) {
// we're iterating from the bottom up, so we need to push these backwards
// iterating from the bottom up, so push dialogue blocks in reverse order
if (match[2]) {

@@ -56,7 +58,7 @@ this.tokens.push({ type: 'dual_dialogue_end' });

this.tokens.push({ type: 'dialogue_end' });
parts = match[3].split(/(\(.+\))(?:\n+)/).reverse();
for (x = 0, xlen = parts.length; x < xlen; x++) {
text = parts[x];
if (text.length > 0) {
this.tokens.push({ type: regex_1.regex.parenthetical.test(text) ? 'parenthetical' : 'dialogue', text: text });
var parts = match[3].split(/(\(.+\))(?:\n+)/).reverse();
for (var _b = 0, parts_1 = parts; _b < parts_1.length; _b++) {
var part = parts_1[_b];
if (part.length > 0) {
this.tokens.push({ type: regex_1.regex.parenthetical.test(part) ? 'parenthetical' : 'dialogue', text: part });
}

@@ -105,3 +107,3 @@ }

}
return this.tokens;
return this.tokens.reverse();
};

@@ -108,0 +110,0 @@ return Scanner;

export interface Token {
type: string;
is_title?: boolean;
text?: string;

@@ -4,0 +5,0 @@ scene_number?: string;

{
"name": "fountain.ts",
"version": "0.0.1",
"version": "0.1.0",
"description": "A Typescript based parser for the screenplay format Fountain. Source originally from Matt Daly's fountain.js",

@@ -8,3 +8,4 @@ "main": "dist/index.js",

"build": "tsc -p .",
"test": "tsc -b test/ && mocha test/dist/test/*.js"
"test": "tsc -b test/ && mocha test/dist/test/*.js",
"playground": "tsc -b test/ && node test/dist/test/fountiants_playground.js"
},

@@ -17,4 +18,11 @@ "keywords": [

],
"repository": {
"type": "git",
"url": "https://github.com/jonnygreenwald/Fountain.ts.git"
},
"author": "Jonny Greenwald",
"contributors": ["Matt Daly", "Jonny Greenwald"],
"contributors": [
"Matt Daly",
"Jonny Greenwald"
],
"license": "MIT",

@@ -21,0 +29,0 @@ "dependencies": {},

@@ -8,3 +8,3 @@ import { Token } from './token';

title: string,
html: {
html: {
title_page: string,

@@ -17,69 +17,66 @@ script: string

export class Fountain {
private title: string;
private tokens: Token[];
private title_page: string[];
private html: string[];
private scanner: Scanner;
private inlineLex: InlineLexer;
constructor() {
this.title_page = [];
this.html = [];
this.scanner = new Scanner;
this.inlineLex = new InlineLexer;
}
public parse(script: string, getTokens?: boolean): Script {
this.tokens = new Scanner().tokenize(script);
this.tokens = this.scanner.tokenize(script);
let title = this.tokens.find(token => token.type === 'title');
let token: Token,
i = this.tokens.length;
return {
title: title ? this.inlineLex.reconstruct(title.text)
.replace('<br />', ' ').replace(/<(?:.|\n)*?>/g, '') : undefined,
html: {
title_page: this.tokens.filter(token => token.is_title).map(token => this.to_html(token)).join(''),
script: this.tokens.filter(token => !token.is_title).map(token => this.to_html(token)).join('')
},
tokens: getTokens ? this.tokens : undefined
}
}
while (i--) {
token = this.tokens[i];
token.text = new InlineLexer().reconstruct(token.text);
public to_html(token: Token): string {
token.text = this.inlineLex.reconstruct(token.text);
switch (token.type) {
case 'title': this.title_page.push('<h1>' + token.text + '</h1>'); this.title = token.text.replace('<br />', ' ').replace(/<(?:.|\n)*?>/g, ''); break;
case 'credit': this.title_page.push('<p class=\"credit\">' + token.text + '</p>'); break;
case 'author': this.title_page.push('<p class=\"authors\">' + token.text + '</p>'); break;
case 'authors': this.title_page.push('<p class=\"authors\">' + token.text + '</p>'); break;
case 'source': this.title_page.push('<p class=\"source\">' + token.text + '</p>'); break;
case 'notes': this.title_page.push('<p class=\"notes\">' + token.text + '</p>'); break;
case 'draft_date': this.title_page.push('<p class=\"draft-date\">' + token.text + '</p>'); break;
case 'date': this.title_page.push('<p class=\"date\">' + token.text + '</p>'); break;
case 'contact': this.title_page.push('<p class=\"contact\">' + token.text + '</p>'); break;
case 'copyright': this.title_page.push('<p class=\"copyright\">' + token.text + '</p>'); break;
switch (token.type) {
case 'title': return '<h1>' + token.text + '</h1>';
case 'credit': return '<p class=\"credit\">' + token.text + '</p>';
case 'author': return '<p class=\"authors\">' + token.text + '</p>';
case 'authors': return '<p class=\"authors\">' + token.text + '</p>';
case 'source': return '<p class=\"source\">' + token.text + '</p>';
case 'notes': return '<p class=\"notes\">' + token.text + '</p>';
case 'draft_date': return '<p class=\"draft-date\">' + token.text + '</p>';
case 'date': return '<p class=\"date\">' + token.text + '</p>';
case 'contact': return '<p class=\"contact\">' + token.text + '</p>';
case 'copyright': return '<p class=\"copyright\">' + token.text + '</p>';
case 'scene_heading': this.html.push('<h3' + (token.scene_number ? ' id=\"' + token.scene_number + '\">' : '>') + token.text + '</h3>'); break;
case 'transition': this.html.push('<h2>' + token.text + '</h2>'); break;
case 'scene_heading': return '<h3' + (token.scene_number ? ' id=\"' + token.scene_number + '\">' : '>') + token.text + '</h3>';
case 'transition': return '<h2>' + token.text + '</h2>';
case 'dual_dialogue_begin': this.html.push('<div class=\"dual-dialogue\">'); break;
case 'dialogue_begin': this.html.push('<div class=\"dialogue' + (token.dual ? ' ' + token.dual : '') + '\">'); break;
case 'character': this.html.push('<h4>' + token.text + '</h4>'); break;
case 'parenthetical': this.html.push('<p class=\"parenthetical\">' + token.text + '</p>'); break;
case 'dialogue': this.html.push('<p>' + token.text + '</p>'); break;
case 'dialogue_end': this.html.push('</div>'); break;
case 'dual_dialogue_end': this.html.push('</div>'); break;
case 'dual_dialogue_begin': return '<div class=\"dual-dialogue\">';
case 'dialogue_begin': return '<div class=\"dialogue' + (token.dual ? ' ' + token.dual : '') + '\">';
case 'character': return '<h4>' + token.text + '</h4>';
case 'parenthetical': return '<p class=\"parenthetical\">' + token.text + '</p>';
case 'dialogue': return '<p>' + token.text + '</p>';
case 'dialogue_end': return '</div>';
case 'dual_dialogue_end': return '</div>';
case 'section': this.html.push('<p class=\"section\" data-depth=\"' + token.depth + '\">' + token.text + '</p>'); break;
case 'synopsis': this.html.push('<p class=\"synopsis\">' + token.text + '</p>'); break;
case 'section': return '<p class=\"section\" data-depth=\"' + token.depth + '\">' + token.text + '</p>';
case 'synopsis': return '<p class=\"synopsis\">' + token.text + '</p>';
case 'note': this.html.push('<!-- ' + token.text + '-->'); break;
case 'boneyard_begin': this.html.push('<!-- '); break;
case 'boneyard_end': this.html.push(' -->'); break;
case 'note': return '<!-- ' + token.text + ' -->';
case 'boneyard_begin': return '<!-- ';
case 'boneyard_end': return ' -->';
case 'action': this.html.push('<p>' + token.text + '</p>'); break;
case 'centered': this.html.push('<p class=\"centered\">' + token.text + '</p>'); break;
case 'page_break': this.html.push('<hr />'); break;
case 'line_break': this.html.push('<br />'); break;
}
}
case 'action': return '<p>' + token.text + '</p>';
case 'centered': return '<p class=\"centered\">' + token.text + '</p>';
return {
title: this.title,
html: {
title_page: this.title_page.join(''),
script: this.html.join('')
},
tokens: getTokens ? this.tokens.reverse() : undefined
case 'page_break': return '<hr />';
case 'line_break': return '<br />';
}
}
}

@@ -15,5 +15,5 @@ import { regex } from './regex';

note: '<!-- $1 -->',
line_break: '<br />',
bold_italic_underline: '<span class=\"bold italic underline\">$2</span>',

@@ -28,25 +28,20 @@ bold_underline: '<span class=\"bold underline\">$2</span>',

public reconstruct(text: string): string {
if (!text) {
return;
}
public reconstruct(line: string): string {
if (!line) return;
const styles = [ 'underline', 'italic', 'bold', 'bold_italic', 'italic_underline', 'bold_underline', 'bold_italic_underline' ];
let i: number = styles.length;
let match: RegExp;
const styles = ['bold_italic_underline', 'bold_underline', 'italic_underline', 'bold_italic', 'bold', 'italic', 'underline'];
let style, match;
text = text.replace(regex.note_inline, this.inline.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, this.inline.line_break);
line = line.replace(regex.note_inline, this.inline.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, this.inline.line_break);
while (i--) {
style = styles[i];
for (let style of styles) {
match = regex[style];
if (match.test(text)) {
text = text.replace(match, this.inline[style]);
if (match.test(line)) {
line = line.replace(match, this.inline[style]);
}
}
return text.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
return line.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
}
}

@@ -7,24 +7,13 @@ import { regex } from './regex';

export class Scanner {
private tokens: Token[];
private tokens: Token[] = [];
constructor() {
this.tokens = [];
}
public tokenize(script: string): Token[] {
// reverse the array so that dual dialog can be constructed bottom up
const source: string[] = new Lexer().reconstruct(script).split(regex.splitter).reverse();
public tokenize(script: string) {
let src = new Lexer().reconstruct(script).split(regex.splitter),
line: string,
match: string[],
parts: string[],
text: string,
meta: any,
x: number,
xlen: number,
let line: string,
match: string[],
dual: boolean;
let i = src.length;
while (i--) {
line = src[i];
for (line of source) {
/** title page */

@@ -34,5 +23,6 @@ if (regex.title_page.test(line)) {

for (x = 0, xlen = match.length; x < xlen; x++) {
parts = match[x].replace(regex.cleaner, '').split(/\:\n*/);
this.tokens.push({ type: parts[0].trim().toLowerCase().replace(' ', '_'), text: parts[1].trim() });
for (let item of match) {
let pair = item.replace(regex.cleaner, '').split(/\:\n*/);
this.tokens.push({ type: pair[0].trim().toLowerCase().replace(' ', '_'), is_title: true, text: pair[1].trim() });
}

@@ -44,3 +34,4 @@ continue;

if (match = line.match(regex.scene_heading)) {
text = match[1] || match[2];
let text: string = match[1] || match[2],
meta: any;

@@ -73,3 +64,3 @@ if (text.indexOf(' ') !== text.length - 2) {

if (match[1].indexOf(' ') !== match[1].length - 2) {
// we're iterating from the bottom up, so we need to push these backwards
// iterating from the bottom up, so push dialogue blocks in reverse order
if (match[2]) {

@@ -81,9 +72,7 @@ this.tokens.push({ type: 'dual_dialogue_end' });

parts = match[3].split(/(\(.+\))(?:\n+)/).reverse();
let parts: string[] = match[3].split(/(\(.+\))(?:\n+)/).reverse();
for (x = 0, xlen = parts.length; x < xlen; x++) {
text = parts[x];
if (text.length > 0) {
this.tokens.push({ type: regex.parenthetical.test(text) ? 'parenthetical' : 'dialogue', text: text });
for (let part of parts) {
if (part.length > 0) {
this.tokens.push({ type: regex.parenthetical.test(part) ? 'parenthetical' : 'dialogue', text: part });
}

@@ -100,3 +89,2 @@ }

dual = match[2] ? true : false;
continue;

@@ -120,11 +108,11 @@ }

if (match = line.match(regex.note)) {
this.tokens.push({ type: 'note', text: match[1]});
this.tokens.push({ type: 'note', text: match[1] });
continue;
}
}
/** boneyard */
/** boneyard */
if (match = line.match(regex.boneyard)) {
this.tokens.push({ type: match[0][0] === '/' ? 'boneyard_begin' : 'boneyard_end' });
continue;
}
}

@@ -146,4 +134,4 @@ /** page breaks */

return this.tokens;
return this.tokens.reverse();
}
}
export interface Token {
type: string,
is_title?: boolean,
text?: string,

@@ -4,0 +5,0 @@ scene_number?: string,

{
"compilerOptions": {
"declaration": true,
"lib": [ "ES2016" ],
"target": "ES5",
"module": "commonjs",
"moduleResolution": "node",

@@ -7,0 +7,0 @@ "outDir": "dist"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc