├── .gitattributes ├── .gitignore ├── README.md ├── bin └── jsml ├── doc ├── LANG.md └── TODO.md ├── examples ├── demo.jsml ├── demo_file.jsml ├── demo_js.jsml ├── demo_short.jsml ├── demo_short_single_child.jsml └── lorem.txt ├── index.js ├── jsml.png ├── jsml.svg ├── lib ├── common.js └── optparse.js └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Test JSML Folders 2 | 3 | ignore 4 | 5 | # node stuff 6 | 7 | node_modules 8 | 9 | # JSML tarball 10 | 11 | node-jsml-*.tgz 12 | 13 | # Windows image file caches 14 | Thumbs.db 15 | ehthumbs.db 16 | 17 | # Folder config file 18 | Desktop.ini 19 | 20 | # Recycle Bin used on file shares 21 | $RECYCLE.BIN/ 22 | 23 | # Windows Installer files 24 | *.cab 25 | *.msi 26 | *.msm 27 | *.msp 28 | 29 | # Windows shortcuts 30 | *.lnk 31 | 32 | # ========================= 33 | # Operating System Files 34 | # ========================= 35 | 36 | # OSX 37 | # ========================= 38 | 39 | .DS_Store 40 | .AppleDouble 41 | .LSOverride 42 | 43 | # Thumbnails 44 | ._* 45 | 46 | # Files that might appear on external disk 47 | .Spotlight-V100 48 | .Trashes 49 | 50 | # Directories potentially created on remote AFP share 51 | .AppleDB 52 | .AppleDesktop 53 | Network Trash Folder 54 | Temporary Items 55 | .apdisk 56 | 57 | # Compiled html 58 | *.html 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### ![JSML](/jsml.png) 2 | 3 | JSML (JSON Markup Language) is an investigation into the effectiveness of creating webpages in JSON. 4 | 5 | [![npm version](https://img.shields.io/npm/v/node-jsml.svg)](https://www.npmjs.com/package/node-jsml) 6 | [![license](https://img.shields.io/npm/l/node-jsml.svg)](https://www.npmjs.com/package/node-jsml) 7 | [![downloads](https://img.shields.io/npm/dm/node-jsml.svg)](https://www.npmjs.com/package/node-jsml) 8 | 9 | ## Installation 10 | 11 | As JSML is on npm, you can install it easily with the command `npm install -g node-jsml`. 12 | 13 | On some systems, superuser access might be required. 14 | 15 | ## Use 16 | 17 | ### Through the Terminal 18 | 19 | To compile a JSML (a `.jsml` file) into an HTML file, run `jsml [filename]`. 20 | 21 | If you want to output to stdout, use `-c` in front of the file, and `-O` to directly specify the output path. 22 | 23 | ### Through Your Program 24 | 25 | To include JSML, use `jsml = require('node-jsml');` 26 | 27 | To compile a JSML file to HTML, use the following code 28 | 29 | ```JavaScript 30 | 31 | var jsml = require('node-jsml'); 32 | 33 | myJSML = [ 34 | { 35 | t:"p", 36 | T:"this works!" 37 | } 38 | ]; 39 | 40 | output = jsml.parse(myJSML); 41 | ``` 42 | 43 | `parse` has an optional parameter, `eval`. If `true`, then `myJSML` (f.e. in this example) will be run though [eval](http://www.w3schools.com/jsref/jsref_eval.asp). This is helpful when reading `.jsml` files which don't have `"` around every tag. 44 | 45 | ## Syntax and Language 46 | 47 | All the rules and tricks can be found in [`LANG.md`](doc/LANG.md) 48 | 49 | ## Inspiration 50 | 51 | When using APIs, one lets out a groan when if they see the API outputs data in XML format. JSON is expected, nowadays, when using an API. Nobody has this problem when writing HTML, however. Perhaps this is because writing a webpage in XML format is better than writing it in JSON. But, until now, there has been no way to tell. And thus, JSML was born. 52 | 53 | This isn't meant to overtake web design as we know it. It's more of a proof of concept. However, JavaScript code can actually be executed within the JSML file, which is kind of neat. One could make counters, dynamic variables, etc. The possibilities are unexplored and endless! 54 | 55 | ## Easy Coding 56 | 57 | There is a Sublime Text 3 plugin I created which makes writing JSML code a lot faster. You can see it [here](https://github.com/mjkaufer/JSML-Formatter). You can find it in sublime's package control by searching for `JSML Formatter`. 58 | 59 | ## Contributing 60 | 61 | If you have something very major to contribute, submit an issue first. Otherwise, for any small fixes, feel free to send a pull request. 62 | 63 | If you'd like to find issues to work on, check [`TODO.md`](doc/TODO.md). 64 | 65 | **Note:** The logo (svg source file) uses the font [monofur](http://www.dafont.com/monofur.font). 66 | -------------------------------------------------------------------------------- /bin/jsml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | optparse = require('../lib/optparse'), 5 | pac = require('../package.json'), 6 | common = require('../lib/common.js'); 7 | 8 | var parser = new optparse.OptionParser([ 9 | ['-h', '--help', 'Shows help screen'], 10 | ['-O', '--output FILE', 'File output, no argument is stdout'], 11 | ['-c', '--compile FILE', 'compiles .json to .html'] 12 | ]); 13 | 14 | var input, output; 15 | 16 | function showHelp(){ 17 | console.log('Usage: jsml -c FILE [-O FILE]\n'+ 18 | 'JSON Markup Language, or jsml is an investigation into the effectiveness of creating webpages in JSON.\n\n'+ 19 | 'Command Line Arguments:\n'+ 20 | ' -h --help This page\n'+ 21 | ' -O --output HTML file\n'+ 22 | ' -c --compile JSML file\n\n'+ 23 | 'version '+pac.version+', (c) 2015\n'+ 24 | 'Source: '+pac.homepage 25 | ); 26 | } 27 | 28 | if(process.argv.length == 2)//not enough arguments 29 | showHelp(); 30 | 31 | parser.on('help', function() { 32 | showHelp(); 33 | }); 34 | 35 | parser.on('output', function(opt, val) { 36 | output = val; 37 | }); 38 | 39 | parser.on('compile', function(opt, val) { 40 | if (!opt) { // No input file has been specified. 41 | console.error("Specify compile file with option '-C'."); 42 | process.exit(1); 43 | } 44 | 45 | outputJSML(val); 46 | 47 | }); 48 | 49 | parser.on(2, function(val){ //default input 50 | 51 | input = val; 52 | 53 | output = input.replace(/\.jsml$/g, "") + ".html" 54 | if(process.argv.length <= 3) 55 | outputJSML(input); 56 | }); 57 | 58 | parser.on(3, function(val){ 59 | output = val; 60 | outputJSML(input); 61 | }); 62 | 63 | parser.on(function(opt) { 64 | console.log('Error - see `jsml --help`'); 65 | }); 66 | 67 | parser.parse(process.argv); 68 | 69 | function outputJSML(infile){ 70 | fs.readFile(infile || process.stdin, 'utf8', function(err, json) { 71 | if (err) { 72 | console.error(err); 73 | process.exit(1); 74 | } 75 | 76 | var jsml = common.parse(json, true); 77 | 78 | if (!output) console.log(jsml); 79 | else fs.writeFile(output, jsml, function(err) { 80 | if (err) console.error(err); 81 | else 82 | console.log("Done compiling " + input + " - saved as " + output); 83 | }); 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /doc/LANG.md: -------------------------------------------------------------------------------- 1 | ## Syntax and Language 2 | 3 | Each item is either a JSON object that has a `children` object which defines children, attributes and/or various kinds of content or is simply a string which defines the tag's content. 4 | 5 | ### Simple Example 6 | 7 | A JSML file that would look like this: 8 | 9 | ```JavaScript 10 | [ 11 | { 12 | tag: "p", 13 | text: "JSML is cool" 14 | } 15 | ] 16 | ``` 17 | 18 | would compile to: 19 | 20 | ```HTML 21 |

JSML is cool

22 | ``` 23 | 24 | The objects `tag` attribute was used to set the tag and the value of `text` was inserted into it. 25 | 26 | ### Nested Elements 27 | 28 | Nested elements are either attribute definitions or child(-ren) specifications. Here an example: 29 | 30 | ```JavaScript 31 | [ 32 | { 33 | tag: "p", 34 | attributes:{ 35 | "id": "info" 36 | }, 37 | text: "Hello" 38 | }, 39 | { 40 | tag: "p", 41 | children: [ 42 | { 43 | tag: "b", 44 | text: "World" 45 | } 46 | ] 47 | } 48 | ] 49 | ``` 50 | 51 | which compiles to: 52 | 53 | ```HTML 54 |

Hello

55 |

56 | World 57 |

58 | ``` 59 | 60 | It's as simple as it seems, the object from `attributes` is written into the tag itself and the `children` array is loaded into it. 61 | 62 | **None:** If `children` and other content is specified, `children` will be added after the content. 63 | 64 | **Example file:** [Children and Attributes](/examples/demo.jsml) 65 | 66 | ### Dynamic JSML and Evaluation 67 | 68 | ```JavaScript 69 | var name = "mjkaufer"; 70 | 71 | [ 72 | { 73 | tag: "h1", 74 | text: name + " - it works!!" 75 | } 76 | ] 77 | ``` 78 | 79 | Basically, any JavaScript can be run above the first `[` tag, or before the JSML array is initialized. Any JavaScript which *does not produce an output* can be run anywhere. For instance, `var name = "mjkaufer"` is ok to use wherever, but something like `console.log("Matthew")` is only ok before the JSML array is initialized. 80 | 81 | **Example file:** [Dynamic JSML](/examples/demo_js.jsml) 82 | 83 | ### Single Child 84 | 85 | If you have a single child, you don't need to bother creating an array for `c` or `children` - you can populate it with a single element instead. 86 | 87 | ```JavaScript 88 | [ 89 | { 90 | tag:"div", 91 | children:{ 92 | tag:"p", 93 | text:"Single child!" 94 | } 95 | } 96 | ] 97 | ``` 98 | 99 | ### Simple Text 100 | 101 | ```HTML 102 | This is nice 103 | ``` 104 | 105 | ```JavaScript 106 | [ 107 | "This is nice" 108 | ] 109 | ``` 110 | 111 | If you use a string instead of an object within an array, it will be directly inserted into the parent. 112 | 113 | ### Short names 114 | 115 | It is much too much work to type `attributes` & co. every time. The next trick may help: 116 | 117 | ```JavaScript 118 | [ 119 | { 120 | t: "ul", 121 | c: [ 122 | { 123 | t: "li", 124 | T: "This is one paragraph." 125 | } 126 | ] 127 | }, 128 | { 129 | t: "a", 130 | a: [ 131 | "href": "google.com" 132 | ], 133 | T: "And this is a link." 134 | } 135 | ] 136 | ``` 137 | 138 | will turn into 139 | 140 | ```HTML 141 | 144 | And this is a link. 145 | ``` 146 | 147 | `t` is short for `tag`, `c` for `children`, `T` for `text` and `a` for `attributes`. If both were to be specified, the longer one would be chosen. 148 | 149 | **Example file:** [Short tags](/examples/demo_js.jsml) 150 | 151 | ### Load Files into JSML 152 | 153 | with the `file` tag, a file can be specified, relative to the working directory, and will be inserted instead of `text`. 154 | 155 | **Example file:** [Read File](/examples/demo_file.jsml) 156 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | * Unit tests 4 | * Incorporate into express, similar to Jade, so compiling is done by the server 5 | * Editor plugin with syntax highlighting 6 | * Asynchronous `parse` for [index.js](/index.js) 7 | -------------------------------------------------------------------------------- /examples/demo.jsml: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | tag:"head", 4 | children:[ 5 | { 6 | tag:"style", 7 | text:"p{color:#cc0000;font-weight:bold;}" 8 | } 9 | ] 10 | }, 11 | { 12 | tag:"body", 13 | children:[ 14 | { 15 | tag:"p", 16 | attributes:{ 17 | id:"info" 18 | }, 19 | text:"Hello" 20 | }, 21 | { 22 | tag:"p", 23 | children:[ 24 | { 25 | tag:"b", 26 | text:"World" 27 | } 28 | ] 29 | }, 30 | { 31 | tag:"div", 32 | attributes:{ 33 | style:"border:solid red 1px;color:#00ff00;" 34 | }, 35 | children:[ 36 | { 37 | tag:"p", 38 | text:"Awesome CSS" 39 | }, 40 | { 41 | tag:"h1", 42 | text:"So cool!" 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | 49 | ] -------------------------------------------------------------------------------- /examples/demo_file.jsml: -------------------------------------------------------------------------------- 1 | [ 2 | "The following text has been read from a file...", 3 | { 4 | "tag" : "code", 5 | "file" : "examples/lorem.txt" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /examples/demo_js.jsml: -------------------------------------------------------------------------------- 1 | var name = "mjkaufer"; 2 | 3 | [ 4 | { 5 | tag:"h1", 6 | text:name + " - it works!!" 7 | } 8 | ] -------------------------------------------------------------------------------- /examples/demo_short.jsml: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | t:"head", 4 | c:[ 5 | { 6 | t:"style", 7 | T:"p{color:#cc0000;font-weight:bold;}" 8 | } 9 | ] 10 | }, 11 | { 12 | t:"body", 13 | c:[ 14 | { 15 | t:"p", 16 | a:{ 17 | id:"info" 18 | }, 19 | T:"Hello" 20 | }, 21 | { 22 | t:"p", 23 | c:[ 24 | { 25 | t:"b", 26 | T:"World" 27 | } 28 | ] 29 | }, 30 | "Just some text", 31 | {t:"em",T:"Whatever"}, 32 | "Me.me", 33 | { 34 | t:"div", 35 | a:{ 36 | style:"border:solid red 1px;color:#00ff00;" 37 | }, 38 | c:[ 39 | { 40 | t:"p", 41 | T:"Awesome CSS" 42 | }, 43 | { 44 | t:"h1", 45 | T:"So cool!" 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | 52 | ] 53 | -------------------------------------------------------------------------------- /examples/demo_short_single_child.jsml: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | t:"head", 4 | c:[ 5 | { 6 | t:"style", 7 | T:"p{color:#cc0000;font-weight:bold;}" 8 | } 9 | ] 10 | }, 11 | { 12 | t:"body", 13 | c:[ 14 | { 15 | t:"p", 16 | a:{ 17 | id:"info" 18 | }, 19 | T:"Hey" 20 | }, 21 | { 22 | t:"p", 23 | c: { 24 | t:"b", 25 | T:"Single Child!" 26 | } 27 | }, 28 | "Just some text", 29 | {t:"em",T:"Whatever"}, 30 | "Me.me", 31 | { 32 | t:"div", 33 | a:{ 34 | style:"border:solid red 1px;color:#00ff00;" 35 | }, 36 | c:[ 37 | { 38 | t:"p", 39 | T:"Awesome CSS" 40 | }, 41 | { 42 | t:"h1", 43 | T:"So cool!" 44 | } 45 | ] 46 | } 47 | ] 48 | } 49 | 50 | ] 51 | -------------------------------------------------------------------------------- /examples/lorem.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var compile = require('./lib/common.js'); 2 | 3 | module.exports.parse = compile.parse; 4 | -------------------------------------------------------------------------------- /jsml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjkaufer/JSML/3f4b360d27841e1cb6f0276e99e45d006c79de9f/jsml.png -------------------------------------------------------------------------------- /jsml.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | JSML Logo 24 | 26 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | JSML Logo 55 | 56 | 57 | Philip Kaludercic <philippija@gmail.com> 58 | 59 | 60 | 62 | 63 | 64 | JSML 65 | 66 | 67 | Logo for the JSON Markup language 68 | 69 | 71 | 73 | 75 | 77 | 79 | 81 | 82 | 83 | 84 | 89 | JSML JSON Markup Language 114 | 115 | 116 | -------------------------------------------------------------------------------- /lib/common.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | path = require('path'); 3 | 4 | String.prototype.isAString = true; 5 | 6 | function handleArray(json, tabs) { 7 | var body = ""; 8 | 9 | for (var ei in json) 10 | body += handleTag(json[ei], tabs+1); 11 | 12 | return body; 13 | } 14 | 15 | function handleTag(json, tabs) { 16 | if (!json.isAString && (!json.tag && !json.t)){ 17 | console.error("Need a tag."); 18 | console.log(json); 19 | process.exit(1); 20 | } 21 | 22 | // Tabs 23 | var t = '\n'+(Array(tabs+1).join('\t')); 24 | 25 | // If object is a string, it will be added to document as is 26 | if (json.isAString) 27 | return t + json; 28 | 29 | // It will be what we concatenate to our elements to make it look all pretty & indented 30 | var start = "<" + (json.tag || json.t); 31 | for (var key in (json.attributes || json.a || [])) 32 | if (json.attributes[key]) 33 | start+=" "+key+"=\"" + json.attributes[key] + "\""; // add the attribute value 34 | 35 | var text; 36 | if (json.file || json.f) 37 | text = fs.readFileSync(json.file || json.f); 38 | else 39 | text = json.text || json.T || ""; 40 | 41 | var end; 42 | if (!text && !(json.children || json.c)) { 43 | start += '/>' 44 | end = ''; 45 | } else { 46 | start += '>' 47 | end = ""; 48 | } 49 | 50 | var children = ""; 51 | if (json.children || json.c) // if there are indeed children 52 | if(Array.isArray((json.children || json.c))) // if it's an array 53 | children = handleArray((json.children || json.c), tabs)+t; 54 | else // it's an json chunk 55 | children = handleTag((json.children || json.c), tabs+1)+t; 56 | 57 | return t + start + text + children + end; 58 | } 59 | 60 | module.exports = { 61 | parse : function(json, evl) { 62 | if (evl) json = eval(json); 63 | 64 | var jsml = "\n"; 65 | jsml += handleArray(json, 0); 66 | jsml += "\n"; 67 | 68 | return jsml; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /lib/optparse.js: -------------------------------------------------------------------------------- 1 | //Optparse.js 1.0.3-Copyright (c)2009 Johan Dahlberg 2 | //Based on:https://github.com/jfd/optparse-js 3 | //Compressed and removed some redundant features by @phikal 4 | var optparse={};try{optparse=exports;}catch(e){}(function(self){var VERSION='1.0.3';var LONG_SWITCH_RE= 5 | /^--\w/,SHORT_SWITCH_RE=/^-\w/,EXT_RULE_RE=/(\-\-[\w_-]+)\s+([\w\[\]_-]+)|(\-\-[\w_-]+)/,ARG_OPTIONAL_RE 6 | =/\[(.+)\]/,DEFAULT_FILTER='_DEFAULT',PREDEFINED_FILTERS={};function filter_text(value){return value;} 7 | PREDEFINED_FILTERS[DEFAULT_FILTER]=filter_text;PREDEFINED_FILTERS.TEXT=filter_text;function build_rules 8 | (filters,arr){var rules=[];for(var i=0;i0)if(LONG_SWITCH_RE.test(arr[l]))return true;return false;}function extend(dest,src){ 19 | var result=dest;for(var n in src)result[n]=src[n];return result;}function spaces(arg1,arg2){var l,builder=[];if( 20 | arg1.constructor===Number)l=arg1;else{if(arg1.length==arg2)return arg1;l=arg2-arg1.length;builder.push(arg1);} 21 | while(l-- > 0)builder.push(' ');return builder.join('');}function Parser(rules){return new OptionParser(rules);} 22 | function OptError(msg){this.toString=function(){return this.msg;};}function OptionParser(rules){this.banner= 23 | 'Usage:[Options]';this.options_title='Available options:';this._rules=rules;this._halt=false;this.filters=extend( 24 | {},PREDEFINED_FILTERS);this.on_args={};this.on_switches={};this.on_halt=function(){};this.default_handler= 25 | function(){};}OptionParser.prototype={on:function(value,fn){if(value.constructor===Function){this.default_handler= 26 | value;}else if(value.constructor===Number){this.on_args[value]=fn;}else{this.on_switches[value]=fn;}},filter: 27 | function(name,fn){this.filters[name.toUpperCase()]=fn;},parse:function(args){var result=[],callback;var rules= 28 | build_rules(this.filters,this._rules);var tokens=args.concat([]);var token;while(this._halt===false&&(token= 29 | tokens.shift())){if(LONG_SWITCH_RE.test(token)||SHORT_SWITCH_RE.test(token)){var arg;for(var i=0;ilongest)longest=rule.decl. 40 | length;}for(var j=0;j", 20 | "contributors" : [ 21 | "Philip Kaludercic " 22 | ], 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/mjkaufer/JSML/issues" 26 | }, 27 | "homepage": "https://github.com/mjkaufer/JSML", 28 | "bin": { 29 | "jsml": "./bin/jsml" 30 | }, 31 | "files": [ 32 | "README.md", 33 | "bin/", 34 | "lib/", 35 | "index.js" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------