123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
- // Distributed under an MIT license: http://codemirror.net/LICENSE
-
- (function(mod) {
- if (typeof exports == 'object' && typeof module == 'object') { // CommonJS
- mod(require('../../lib/codemirror'));
- } else if (typeof define == 'function' && define.amd) { // AMD
- define(['../../lib/codemirror'], mod);
- } else { // Plain browser env
- mod(CodeMirror);
- }
- })(function(CodeMirror) {
- 'use strict';
-
- var TOKEN_STYLES = {
- addition: 'positive',
- attributes: 'attribute',
- bold: 'strong',
- cite: 'keyword',
- code: 'atom',
- definitionList: 'number',
- deletion: 'negative',
- div: 'punctuation',
- em: 'em',
- footnote: 'variable',
- footCite: 'qualifier',
- header: 'header',
- html: 'comment',
- image: 'string',
- italic: 'em',
- link: 'link',
- linkDefinition: 'link',
- list1: 'variable-2',
- list2: 'variable-3',
- list3: 'keyword',
- notextile: 'string-2',
- pre: 'operator',
- p: 'property',
- quote: 'bracket',
- span: 'quote',
- specialChar: 'tag',
- strong: 'strong',
- sub: 'builtin',
- sup: 'builtin',
- table: 'variable-3',
- tableHeading: 'operator'
- };
-
- function Parser(regExpFactory, state, stream) {
- this.regExpFactory = regExpFactory;
- this.state = state;
- this.stream = stream;
- this.styles = TOKEN_STYLES;
-
- this.state.specialChar = null;
- }
-
- Parser.prototype.eat = function(name) {
- return this.stream.match(this.regExpFactory.pattern(name), true);
- };
-
- Parser.prototype.check = function(name) {
- return this.stream.match(this.regExpFactory.pattern(name), false);
- };
-
- Parser.prototype.setModeForNextToken = function(mode) {
- return this.state.mode = mode;
- };
-
- Parser.prototype.execMode = function(newMode) {
- return this.setModeForNextToken(newMode).call(this);
- };
-
- Parser.prototype.startNewLine = function() {
- this.setModeForNextToken(Modes.newLayout);
- this.state.tableHeading = false;
-
- if (this.state.layoutType === 'definitionList' && this.state.spanningLayout) {
- if (this.check('definitionListEnd')) {
- this.state.spanningLayout = false;
- }
- }
- };
-
- Parser.prototype.nextToken = function() {
- return this.state.mode.call(this);
- };
-
- Parser.prototype.styleFor = function(token) {
- if (this.styles.hasOwnProperty(token)) {
- return this.styles[token];
- }
- throw 'unknown token';
- };
-
- Parser.prototype.handlePhraseModifier = function(ch) {
- if (ch === '_') {
- if (this.stream.eat('_')) {
- return this.togglePhraseModifier('italic', /^.*__/);
- }
- return this.togglePhraseModifier('em', /^.*_/);
- }
-
- if (ch === '*') {
- if (this.stream.eat('*')) {
- return this.togglePhraseModifier('bold', /^.*\*\*/);
- }
- return this.togglePhraseModifier('strong', /^.*\*/);
- }
-
- if (ch === '[') {
- if (this.stream.match(/\d+\]/)) {
- this.state.footCite = true;
- }
- return this.tokenStyles();
- }
-
- if (ch === '(') {
- if (this.stream.match('r)')) {
- this.state.specialChar = 'r';
- } else if (this.stream.match('tm)')) {
- this.state.specialChar = 'tm';
- } else if (this.stream.match('c)')) {
- this.state.specialChar = 'c';
- }
- return this.tokenStyles();
- }
-
- if (ch === '<') {
- if (this.stream.match(/(\w+)[^>]+>[^<]+<\/\1>/)) {
- return this.tokenStylesWith(this.styleFor('html'));
- }
- }
-
- if (ch === '?' && this.stream.eat('?')) {
- return this.togglePhraseModifier('cite', /^.*\?\?/);
- }
- if (ch === '=' && this.stream.eat('=')) {
- return this.togglePhraseModifier('notextile', /^.*==/);
- }
- if (ch === '-') {
- return this.togglePhraseModifier('deletion', /^.*-/);
- }
- if (ch === '+') {
- return this.togglePhraseModifier('addition', /^.*\+/);
- }
- if (ch === '~') {
- return this.togglePhraseModifier('sub', /^.*~/);
- }
- if (ch === '^') {
- return this.togglePhraseModifier('sup', /^.*\^/);
- }
- if (ch === '%') {
- return this.togglePhraseModifier('span', /^.*%/);
- }
- if (ch === '@') {
- return this.togglePhraseModifier('code', /^.*@/);
- }
- if (ch === '!') {
- var type = this.togglePhraseModifier('image', /^.*(?:\([^\)]+\))?!/);
- this.stream.match(/^:\S+/); // optional Url portion
- return type;
- }
- return this.tokenStyles();
- };
-
- Parser.prototype.togglePhraseModifier = function(phraseModifier, closeRE) {
- if (this.state[phraseModifier]) { // remove phrase modifier
- var type = this.tokenStyles();
- this.state[phraseModifier] = false;
- return type;
- }
- if (this.stream.match(closeRE, false)) { // add phrase modifier
- this.state[phraseModifier] = true;
- this.setModeForNextToken(Modes.attributes);
- }
- return this.tokenStyles();
- };
-
- Parser.prototype.tokenStyles = function() {
- var disabled = this.textileDisabled(),
- styles = [];
-
- if (disabled) return disabled;
-
- if (this.state.layoutType) {
- styles.push(this.styleFor(this.state.layoutType));
- }
-
- styles = styles.concat(this.activeStyles('addition', 'bold', 'cite', 'code',
- 'deletion', 'em', 'footCite', 'image', 'italic', 'link', 'span', 'specialChar', 'strong',
- 'sub', 'sup', 'table', 'tableHeading'));
-
- if (this.state.layoutType === 'header') {
- styles.push(this.styleFor('header') + '-' + this.state.header);
- }
- return styles.length ? styles.join(' ') : null;
- };
-
- Parser.prototype.textileDisabled = function() {
- var type = this.state.layoutType;
-
- switch(type) {
- case 'notextile':
- case 'code':
- case 'pre':
- return this.styleFor(type);
- default:
- if (this.state.notextile) {
- return this.styleFor('notextile') + (type ? (' ' + this.styleFor(type)) : '');
- }
-
- return null;
- }
- };
-
- Parser.prototype.tokenStylesWith = function(extraStyles) {
- var disabled = this.textileDisabled(),
- type;
-
- if (disabled) return disabled;
-
- type = this.tokenStyles();
- if(extraStyles) {
- return type ? (type + ' ' + extraStyles) : extraStyles;
- }
- return type;
- };
-
- Parser.prototype.activeStyles = function() {
- var styles = [],
- i;
- for (i = 0; i < arguments.length; ++i) {
- if (this.state[arguments[i]]) {
- styles.push(this.styleFor(arguments[i]));
- }
- }
- return styles;
- };
-
- Parser.prototype.blankLine = function() {
- var spanningLayout = this.state.spanningLayout,
- type = this.state.layoutType,
- key;
-
- for (key in this.state) {
- if (this.state.hasOwnProperty(key)) {
- delete this.state[key];
- }
- }
-
- this.setModeForNextToken(Modes.newLayout);
- if (spanningLayout) {
- this.state.layoutType = type;
- this.state.spanningLayout = true;
- }
- };
-
-
- function RegExpFactory() {
- this.cache = {};
- this.single = {
- bc: 'bc',
- bq: 'bq',
- definitionList: /- [^(?::=)]+:=+/,
- definitionListEnd: /.*=:\s*$/,
- div: 'div',
- drawTable: /\|.*\|/,
- foot: /fn\d+/,
- header: /h[1-6]/,
- html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/,
- link: /[^"]+":\S/,
- linkDefinition: /\[[^\s\]]+\]\S+/,
- list: /(?:#+|\*+)/,
- notextile: 'notextile',
- para: 'p',
- pre: 'pre',
- table: 'table',
- tableCellAttributes: /[/\\]\d+/,
- tableHeading: /\|_\./,
- tableText: /[^"_\*\[\(\?\+~\^%@|-]+/,
- text: /[^!"_=\*\[\(<\?\+~\^%@-]+/
- };
- this.attributes = {
- align: /(?:<>|<|>|=)/,
- selector: /\([^\(][^\)]+\)/,
- lang: /\[[^\[\]]+\]/,
- pad: /(?:\(+|\)+){1,2}/,
- css: /\{[^\}]+\}/
- };
- }
-
- RegExpFactory.prototype.pattern = function(name) {
- return (this.cache[name] || this.createRe(name));
- };
-
- RegExpFactory.prototype.createRe = function(name) {
- switch (name) {
- case 'drawTable':
- return this.makeRe('^', this.single.drawTable, '$');
- case 'html':
- return this.makeRe('^', this.single.html, '(?:', this.single.html, ')*', '$');
- case 'linkDefinition':
- return this.makeRe('^', this.single.linkDefinition, '$');
- case 'listLayout':
- return this.makeRe('^', this.single.list, this.pattern('allAttributes'), '*\\s+');
- case 'tableCellAttributes':
- return this.makeRe('^', this.choiceRe(this.single.tableCellAttributes,
- this.pattern('allAttributes')), '+\\.');
- case 'type':
- return this.makeRe('^', this.pattern('allTypes'));
- case 'typeLayout':
- return this.makeRe('^', this.pattern('allTypes'), this.pattern('allAttributes'),
- '*\\.\\.?', '(\\s+|$)');
- case 'attributes':
- return this.makeRe('^', this.pattern('allAttributes'), '+');
-
- case 'allTypes':
- return this.choiceRe(this.single.div, this.single.foot,
- this.single.header, this.single.bc, this.single.bq,
- this.single.notextile, this.single.pre, this.single.table,
- this.single.para);
-
- case 'allAttributes':
- return this.choiceRe(this.attributes.selector, this.attributes.css,
- this.attributes.lang, this.attributes.align, this.attributes.pad);
-
- default:
- return this.makeRe('^', this.single[name]);
- }
- };
-
-
- RegExpFactory.prototype.makeRe = function() {
- var pattern = '',
- i,
- arg;
-
- for (i = 0; i < arguments.length; ++i) {
- arg = arguments[i];
- pattern += (typeof arg === 'string') ? arg : arg.source;
- }
- return new RegExp(pattern);
- };
-
- RegExpFactory.prototype.choiceRe = function() {
- var parts = [arguments[0]],
- i;
-
- for (i = 1; i < arguments.length; ++i) {
- parts[i * 2 - 1] = '|';
- parts[i * 2] = arguments[i];
- }
-
- parts.unshift('(?:');
- parts.push(')');
- return this.makeRe.apply(this, parts);
- };
-
-
- var Modes = {
- newLayout: function() {
- if (this.check('typeLayout')) {
- this.state.spanningLayout = false;
- return this.execMode(Modes.blockType);
- }
- if (!this.textileDisabled()) {
- if (this.check('listLayout')) {
- return this.execMode(Modes.list);
- } else if (this.check('drawTable')) {
- return this.execMode(Modes.table);
- } else if (this.check('linkDefinition')) {
- return this.execMode(Modes.linkDefinition);
- } else if (this.check('definitionList')) {
- return this.execMode(Modes.definitionList);
- } else if (this.check('html')) {
- return this.execMode(Modes.html);
- }
- }
- return this.execMode(Modes.text);
- },
-
- blockType: function() {
- var match,
- type;
- this.state.layoutType = null;
-
- if (match = this.eat('type')) {
- type = match[0];
- } else {
- return this.execMode(Modes.text);
- }
-
- if(match = type.match(this.regExpFactory.pattern('header'))) {
- this.state.layoutType = 'header';
- this.state.header = parseInt(match[0][1]);
- } else if (type.match(this.regExpFactory.pattern('bq'))) {
- this.state.layoutType = 'quote';
- } else if (type.match(this.regExpFactory.pattern('bc'))) {
- this.state.layoutType = 'code';
- } else if (type.match(this.regExpFactory.pattern('foot'))) {
- this.state.layoutType = 'footnote';
- } else if (type.match(this.regExpFactory.pattern('notextile'))) {
- this.state.layoutType = 'notextile';
- } else if (type.match(this.regExpFactory.pattern('pre'))) {
- this.state.layoutType = 'pre';
- } else if (type.match(this.regExpFactory.pattern('div'))) {
- this.state.layoutType = 'div';
- } else if (type.match(this.regExpFactory.pattern('table'))) {
- this.state.layoutType = 'table';
- }
-
- this.setModeForNextToken(Modes.attributes);
- return this.tokenStyles();
- },
-
- text: function() {
- if (this.eat('text')) {
- return this.tokenStyles();
- }
-
- var ch = this.stream.next();
-
- if (ch === '"') {
- return this.execMode(Modes.link);
- }
- return this.handlePhraseModifier(ch);
- },
-
- attributes: function() {
- this.setModeForNextToken(Modes.layoutLength);
-
- if (this.eat('attributes')) {
- return this.tokenStylesWith(this.styleFor('attributes'));
- }
- return this.tokenStyles();
- },
-
- layoutLength: function() {
- if (this.stream.eat('.') && this.stream.eat('.')) {
- this.state.spanningLayout = true;
- }
-
- this.setModeForNextToken(Modes.text);
- return this.tokenStyles();
- },
-
- list: function() {
- var match = this.eat('list'),
- listMod;
- this.state.listDepth = match[0].length;
- listMod = (this.state.listDepth - 1) % 3;
- if (!listMod) {
- this.state.layoutType = 'list1';
- } else if (listMod === 1) {
- this.state.layoutType = 'list2';
- } else {
- this.state.layoutType = 'list3';
- }
- this.setModeForNextToken(Modes.attributes);
- return this.tokenStyles();
- },
-
- link: function() {
- this.setModeForNextToken(Modes.text);
- if (this.eat('link')) {
- this.stream.match(/\S+/);
- return this.tokenStylesWith(this.styleFor('link'));
- }
- return this.tokenStyles();
- },
-
- linkDefinition: function() {
- this.stream.skipToEnd();
- return this.tokenStylesWith(this.styleFor('linkDefinition'));
- },
-
- definitionList: function() {
- this.eat('definitionList');
-
- this.state.layoutType = 'definitionList';
-
- if (this.stream.match(/\s*$/)) {
- this.state.spanningLayout = true;
- } else {
- this.setModeForNextToken(Modes.attributes);
- }
- return this.tokenStyles();
- },
-
- html: function() {
- this.stream.skipToEnd();
- return this.tokenStylesWith(this.styleFor('html'));
- },
-
- table: function() {
- this.state.layoutType = 'table';
- return this.execMode(Modes.tableCell);
- },
-
- tableCell: function() {
- if (this.eat('tableHeading')) {
- this.state.tableHeading = true;
- } else {
- this.stream.eat('|');
- }
- this.setModeForNextToken(Modes.tableCellAttributes);
- return this.tokenStyles();
- },
-
- tableCellAttributes: function() {
- this.setModeForNextToken(Modes.tableText);
-
- if (this.eat('tableCellAttributes')) {
- return this.tokenStylesWith(this.styleFor('attributes'));
- }
- return this.tokenStyles();
- },
-
- tableText: function() {
- if (this.eat('tableText')) {
- return this.tokenStyles();
- }
-
- if (this.stream.peek() === '|') { // end of cell
- this.setModeForNextToken(Modes.tableCell);
- return this.tokenStyles();
- }
- return this.handlePhraseModifier(this.stream.next());
- }
- };
-
-
- CodeMirror.defineMode('textile', function() {
- var regExpFactory = new RegExpFactory();
-
- return {
- startState: function() {
- return { mode: Modes.newLayout };
- },
- token: function(stream, state) {
- var parser = new Parser(regExpFactory, state, stream);
- if (stream.sol()) { parser.startNewLine(); }
- return parser.nextToken();
- },
- blankLine: function(state) {
- new Parser(regExpFactory, state).blankLine();
- }
- };
- });
-
- CodeMirror.defineMIME('text/x-textile', 'textile');
- });
|