Skip to content

Commit

Permalink
Made the array-like APIs work when 'length' property is set.
Browse files Browse the repository at this point in the history
  • Loading branch information
henry-luo committed Mar 4, 2019
1 parent 8e92382 commit b8ce5be
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 32 deletions.
8 changes: 4 additions & 4 deletions docs/mark-grammar.html
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ <h2 id='base64_binary'>base64_binary</h2>
<path d="M50 41h0"></path>
<path d="M86 41h0"></path>
<rect x="50" y="30" width="36" height="22" rx="10" ry="10"></rect>
<text x="68" y="45">{:</text>
<text x="68" y="45">&#91;#</text>
</g>
<path d="M86 41h10"></path>
<g>
Expand Down Expand Up @@ -1338,7 +1338,7 @@ <h2 id='base64_binary'>base64_binary</h2>
<path d="M450 41h0"></path>
<path d="M478 41h0"></path>
<rect x="450" y="30" width="28" height="22" rx="10" ry="10"></rect>
<text x="464" y="45">}</text>
<text x="464" y="45">&#93;</text>
</g>
<path d="M478 41h10"></path>
</g>
Expand Down Expand Up @@ -1370,7 +1370,7 @@ <h2 id='ascii85_binary'>ascii85_binary</h2>
<path d="M50 41h0"></path>
<path d="M94 41h0"></path>
<rect x="50" y="30" width="44" height="22" rx="10" ry="10"></rect>
<text x="72" y="45">{:~</text>
<text x="72" y="45">&#91;#~</text>
</g>
<path d="M94 41h10"></path>
<g>
Expand Down Expand Up @@ -1428,7 +1428,7 @@ <h2 id='ascii85_binary'>ascii85_binary</h2>
<path d="M298 41h0"></path>
<path d="M334 41h0"></path>
<rect x="298" y="30" width="36" height="22" rx="10" ry="10"></rect>
<text x="316" y="45">~}</text>
<text x="316" y="45">~&#93;</text>
</g>
<path d="M334 41h10"></path>
</g>
Expand Down
6 changes: 3 additions & 3 deletions docs/mark-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,16 @@ Other syntax extensions made to JSON are pretty much just syntax sugars, some in

### 3.5 Binary Value

- Binary data can be encoded as a sequence of characters delimited by '`{:`' and '`}`'.
- Binary data can be encoded as a sequence of characters delimited by `[#]` and `]`.
- It can encoded in either [base64](https://en.wikipedia.org/wiki/Base64) or [ascii85](https://en.wikipedia.org/wiki/Ascii85) *(Adobe version)* encoding.
- Whitespaces are allowed between the encoded characters and are ignored by the parser.

```BNF
binary ::= base64_binary | ascii85_binary
base64_binary ::= '{:' ([0-9a-zA-Z] | '+' | '/' | ws_char)* '='? '='? '}'
base64_binary ::= '[#' ([0-9a-zA-Z] | '+' | '/' | ws_char)* '='? '='? ']'
ascii85_binary ::= '{:~' ([#x21-u] | 'z' | ws_char)* '~}'
ascii85_binary ::= '[#~' ([#x21-u] | 'z' | ws_char)* '~]'
```

### 3.6 Comments
Expand Down
4 changes: 2 additions & 2 deletions docs/mark.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ digit ::= [0-9]
/*-- binary data -------------------------------------------------*/
binary ::= base64_binary | ascii85_binary

base64_binary ::= '{:' ([0-9a-zA-Z] | '+' | '/' | ws_char)* '='? '='? '}' /* 2 optional padding characters */
base64_binary ::= '[#' ([0-9a-zA-Z] | '+' | '/' | ws_char)* '='? '='? ']' /* 2 optional padding characters */

ascii85_binary ::= '{:~' ([#x21-u] | 'z' | ws_char)* '~}'
ascii85_binary ::= '[#~' ([#x21-u] | 'z' | ws_char)* '~]'

/*-- string ------------------------------------------------------*/
string ::= s_string | d_string | t_string | identifier
Expand Down
2 changes: 1 addition & 1 deletion lib/mark.mutate.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let MarkMutate = function(Mark, pushFunc, lenSymbol, pragSymbol) {
}
},

// similar to Array.slice()
// similar to Array.slice(), but with content normalization
splice(index, cnt) {
if (cnt < 0) { cnt = 0; } // negative cnt treated as zero;
let len = this[$length];
Expand Down
33 changes: 13 additions & 20 deletions mark.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,20 @@

// symbols used internally
const $length = Symbol.for('Mark.length'), // for content length
_length = Symbol.for('Mark.length-property'),
_length = Symbol.for('Mark.length-property'), // for property length
$parent = Symbol.for('Mark.parent'), // for parent object
$pragma = Symbol.for('Mark.pragma'), // for pragma value
$paired = Symbol.for('Mark.pragma-paired');

let $convert = null, // Mark Convert API
$ctrs = {}; // cached constructors for the Mark objects
// patch IE11
if (!$ctrs.constructor.name) { // IE11 does not set constructor.name to 'Object'
$ctrs.constructor.name = 'Object';
}

// MARK is the static Mark API, it is different from the Mark.prototype that Mark object extends
var MARK = (function() {
// patch IE11
if (!$ctrs.constructor.name) { // IE11 does not set constructor.name to 'Object'
$ctrs.constructor.name = 'Object';
}

function push(val) {
let len = this[$length];
let t = typeof val;
Expand Down Expand Up @@ -111,7 +110,6 @@ var MARK = (function() {
};

// Mark object API functions
let ap = Array.prototype;
let api = {
// object 'properties': just use JS Object.keys(), Object.values(), Object.entries() to work with the properties

Expand All @@ -127,19 +125,6 @@ var MARK = (function() {
return this[$parent];
},

// as Mark is array-like, we can simply inherit all functional methods from Array.prototype
filter: ap.filter,
map: ap.map,
reduce: ap.reduce,
every: ap.every,
some: ap.some,
each: ap.forEach,
forEach: ap.forEach,
includes: ap.includes,
indexOf: ap.indexOf,
lastIndexOf: ap.lastIndexOf,
slice: ap.slice,

// conversion APIs
source: function(options) {
return MARK.stringify(this, options);
Expand All @@ -164,6 +149,14 @@ var MARK = (function() {
return MARK.stringify(this, opt);
},
}
// setup array-like APIs
let ap = Array.prototype;
function wrapped(obj) { return Object.create(obj, {length:{value:obj[$length]}}); }
for (let f of [ap.filter, ap.map, ap.reduce, ap.every, ap.some, ap.forEach, ap.includes, ap.indexOf, ap.lastIndexOf, ap.slice]) {
api[f.name] = function() { return f.apply(this[_length] !== null ? wrapped(this):this, arguments); }
}
api['each'] = api.forEach; // alias

// set the APIs
for (let a in api) {
// API functions are non-enumerable
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mark-js",
"version": "0.10.1",
"version": "0.11.0",
"description": "The core library for working with Mark, an unified notation for both object and markup data.",
"main": "mark.js",
"directories": {
Expand Down
35 changes: 34 additions & 1 deletion test/mark-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ test('Mark object model', function(assert) {
assert.equal(Array.isArray(div_contents), true, "Mark object div contents isArray() should be true");
assert.equal(JSON.stringify(div_contents), '["text"]', "Mark object div contents should be ['text']");
assert.equal(arrayEqual(div_contents, ["text"]), true, "Mark object div contents should be ['text']");


// array-like APIs without 'length' property
// filter // length:0
div = Mark.parse('{div "text" {br} "more" {b "bold"} (!-- comment --)}');
assert.deepEqual(div.filter(n => typeof n === 'string'), ["text", "more"], "Mark filter API");
Expand All @@ -84,6 +85,7 @@ test('Mark object model', function(assert) {
// each
let types = []; div.each(n => types.push(typeof n));
assert.deepEqual(types, ["string", "object", "string", "object", "object"], "Mark each API");
// forEach
types = []; div.forEach(n => types.push(typeof n));
assert.deepEqual(types, ["string", "object", "string", "object", "object"], "Mark forEach API");
// includes
Expand All @@ -97,6 +99,37 @@ test('Mark object model', function(assert) {
let items = [div[1], "more"];
assert.deepEqual(div.slice(1,3), items, "Mark slice API");

// array-like APIs with 'length' property set
// filter
div = Mark.parse('{div length:0 "text" {br} "more" {b "bold"} (!-- comment --)}');
assert.deepEqual(div.filter(n => typeof n === 'string'), ["text", "more"], "Mark filter API");
// map
assert.deepEqual(div.map(n => typeof n), ["string", "object", "string", "object", "object"], "Mark map API");
// reduce
assert.equal(div.reduce((result, n, i) => result + (i?', ':'') + (typeof n), 'type: '), "type: string, object, string, object, object", "Mark reduce API");
// every
assert.equal(div.every(n => typeof n != 'number'), true, "Mark every API");
assert.equal(div.every(n => typeof n != 'object'), false, "Mark every API");
// some
assert.equal(div.some(n => n.constructor && n.constructor.name == 'b'), true, "Mark some API");
assert.equal(div.some(n => n.constructor && n.constructor.name == 'div'), false, "Mark some API");
// each
types = []; div.each(n => types.push(typeof n));
assert.deepEqual(types, ["string", "object", "string", "object", "object"], "Mark each API");
// forEach
types = []; div.forEach(n => types.push(typeof n));
assert.deepEqual(types, ["string", "object", "string", "object", "object"], "Mark forEach API");
// includes
assert.equal(div.includes("more"), true, "Mark includes API");
assert.equal(div.includes("test"), false, "Mark includes API");
// indexOf
assert.equal(div.indexOf("more"), 2, "Mark indexOf API");
// lastIndexOf
assert.equal(div.lastIndexOf("more"), 2, "Mark lastIndexOf API");
// slice
items = [div[1], "more"];
assert.deepEqual(div.slice(1,3), items, "Mark slice API");

// direct content assignment - not advisable, as content is not normalized
var div = Mark.parse('{div "text"}');
div[0] = Mark('br');
Expand Down

0 comments on commit b8ce5be

Please sign in to comment.