Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit bd0915c

Browse files
committed
feat(ngParseExt): New ngParseExt module
New ngParseExt module Including this module into an application will extend $parse to allow identifiers following ES6 identifiers
1 parent ad29894 commit bd0915c

12 files changed

+1508
-3
lines changed

Gruntfile.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ module.exports = function(grunt) {
137137
ngMock: {
138138
files: { src: 'src/ngMock/**/*.js' },
139139
},
140+
ngParseExt: {
141+
files: { src: 'src/ngParseExt/**/*.js' },
142+
},
140143
ngResource: {
141144
files: { src: 'src/ngResource/**/*.js' },
142145
},
@@ -233,7 +236,11 @@ module.exports = function(grunt) {
233236
dest: 'build/angular-aria.js',
234237
src: util.wrap(files['angularModules']['ngAria'], 'module')
235238
},
236-
'promises-aplus-adapter': {
239+
parseext: {
240+
dest: 'build/angular-parse-ext.js',
241+
src: util.wrap(files['angularModules']['ngParseExt'], 'module')
242+
},
243+
"promises-aplus-adapter": {
237244
dest:'tmp/promises-aplus-adapter++.js',
238245
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
239246
}
@@ -251,7 +258,8 @@ module.exports = function(grunt) {
251258
resource: 'build/angular-resource.js',
252259
route: 'build/angular-route.js',
253260
sanitize: 'build/angular-sanitize.js',
254-
aria: 'build/angular-aria.js'
261+
aria: 'build/angular-aria.js',
262+
parseext: 'build/angular-parse-ext.js'
255263
},
256264

257265

angularFiles.js

+4
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ var angularFiles = {
120120
'ngMessages': [
121121
'src/ngMessages/messages.js'
122122
],
123+
'ngParseExt': [
124+
'src/ngParseExt/ucd.js',
125+
'src/ngParseExt/module.js'
126+
],
123127
'ngResource': [
124128
'src/ngResource/resource.js'
125129
],

i18n/generate.sh

+4
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ npm run test-i18n
99

1010
node src/closureSlurper.js
1111

12+
npm run test-i18n-ucd
13+
14+
echo "Generating ngParseExt"
15+
node ucd/src/extract.js
1216

i18n/ucd/spec/extactValuesSpec.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var extractValues = require('../src/extractValues.js').extractValues;
2+
var stream = require('stream');
3+
4+
function StringStream(str) {
5+
return new stream.Readable({
6+
read: function(n) {
7+
this.push(str);
8+
str = null;
9+
}
10+
});
11+
}
12+
13+
describe('extractValues', function() {
14+
it('should extract the values from the xml', function(done) {
15+
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="N"></char></repertoire></ucd>';
16+
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
17+
expect(values).toEqual({ IDS_Y : [ [ '0001', '0002' ] ] });
18+
done();
19+
});
20+
});
21+
22+
it('should extract the values from the xml if the last element matches', function(done) {
23+
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="Y"></char></repertoire></ucd>';
24+
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
25+
expect(values).toEqual({ IDS_Y : [ [ '0001', '0003' ] ] });
26+
done();
27+
});
28+
});
29+
30+
it('should support `reserved`', function(done) {
31+
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><reserved first-cp="0002" last-cp="0005" IDS="N"></reserved><char cp="0006" IDS="Y"></char></repertoire></ucd>';
32+
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
33+
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
34+
done();
35+
});
36+
});
37+
38+
it('should support `surrogate`', function(done) {
39+
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><surrogate first-cp="0002" last-cp="0005" IDS="N"></surrogate><char cp="0006" IDS="Y"></char></repertoire></ucd>';
40+
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
41+
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
42+
done();
43+
});
44+
});
45+
46+
it('should support `noncharactere`', function(done) {
47+
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><noncharacter first-cp="0002" last-cp="0005" IDS="N"></noncharacter><char cp="0006" IDS="Y"></char></repertoire></ucd>';
48+
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
49+
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
50+
done();
51+
});
52+
});
53+
});

i18n/ucd/spec/generateCodeSpec.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
var generateCodeModule = require('../src/generateCode.js');
3+
var generateCode = generateCodeModule.generateCode;
4+
var generateFunction = generateCodeModule.generateFunction;
5+
6+
describe('generateFunction', function() {
7+
it('should generate function with ranges', function() {
8+
expect(generateFunction([ [ '0001', '0003' ] ], 'IDS_Y')).toEqual('\
9+
function IDS_Y(cp) {\n\
10+
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
11+
return false;\n\
12+
}\n');
13+
});
14+
15+
it('should generate function with multiple ranges', function() {
16+
expect(generateFunction([ [ '0001', '0003' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
17+
function IDS_Y(cp) {\n\
18+
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
19+
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
20+
return false;\n\
21+
}\n');
22+
});
23+
24+
it('should generate function with unique values', function() {
25+
expect(generateFunction([ [ '0001', '0001' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
26+
function IDS_Y(cp) {\n\
27+
if (cp === 0x0001) return true;\n\
28+
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
29+
return false;\n\
30+
}\n');
31+
});
32+
});
33+
34+
describe('generateCode', function() {
35+
it('should generate the function for all the values', function() {
36+
expect(generateCode({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ], IDC_Y : [ [ '0002', '0002' ], [ '0007', '0007' ] ] })).toEqual('\
37+
/******************************************************\n\
38+
* Generated file, do not modify *\n\
39+
* *\n\
40+
*****************************************************/\n\
41+
"use strict";\n\
42+
function IDS_Y(cp) {\n\
43+
if (cp === 0x0001) return true;\n\
44+
if (cp === 0x0006) return true;\n\
45+
return false;\n\
46+
}\n\
47+
function IDC_Y(cp) {\n\
48+
if (cp === 0x0002) return true;\n\
49+
if (cp === 0x0007) return true;\n\
50+
return false;\n\
51+
}\n\
52+
');
53+
});
54+
});

i18n/ucd/src/extract.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"use strict";
2+
3+
var fs = require('fs');
4+
var zlib = require('zlib');
5+
var extractValues = require('./extractValues').extractValues;
6+
var generateCode = require('./generateCode').generateCode;
7+
var generateextractValues = require('./extractValues').extractValues;
8+
// ID_Start and ID_Continue
9+
var propertiesToExtract = {'IDS': 'Y', 'IDC': 'Y'};
10+
11+
function main() {
12+
extractValues(
13+
fs.createReadStream('./ucd/src/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
14+
propertiesToExtract,
15+
writeFile);
16+
17+
function writeFile(validRanges) {
18+
var code = generateCode(validRanges);
19+
try {
20+
var stats = fs.lstatSync('../src/ngParseExt');
21+
} catch (e) {
22+
fs.mkdirSync('../src/ngParseExt');
23+
}
24+
fs.writeFile('../src/ngParseExt/ucd.js', code);
25+
}
26+
}
27+
28+
main();

i18n/ucd/src/extractValues.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Extract values from a stream.
3+
*/
4+
5+
exports.extractValues = extractValues;
6+
7+
var sax = require('sax/lib/sax');
8+
var saxStrict = true;
9+
var saxOptions = {};
10+
var validXMLTagNames = { char: 'Y', reserved: 'Y', surrogate: 'Y', noncharacter: 'Y'};
11+
12+
function extractValues(stream, propertiesToExtract, callback) {
13+
var saxStream = sax.createStream(saxStrict, saxOptions);
14+
var firstValid = {};
15+
var lastValid = {};
16+
var keys = Object.keys(propertiesToExtract);
17+
var keyValues = keys.map(function(k) { return propertiesToExtract[k]; });
18+
var validRanges = {};
19+
20+
for (var i in keys) {
21+
validRanges[keys[i] + '_' + keyValues[i]] = [];
22+
}
23+
saxStream.onopentag = onOpenTag;
24+
stream
25+
.pipe(saxStream)
26+
.on('end', doCallback);
27+
28+
function onOpenTag(node) {
29+
var property;
30+
if (validXMLTagNames[node.name]) {
31+
for (var i in keys) {
32+
property = keyValues[i];
33+
if (node.attributes[keys[i]] === property) validProperty(keys[i] + '_' + property, node);
34+
else invalidProperty(keys[i] + '_' + property);
35+
}
36+
}
37+
}
38+
39+
function validProperty(property, node) {
40+
if (!firstValid[property]) firstValid[property] =
41+
node.attributes.cp || node.attributes['first-cp'];
42+
lastValid[property] = node.attributes.cp || node.attributes['last-cp'];
43+
}
44+
45+
function invalidProperty(property) {
46+
if (!firstValid[property]) return;
47+
validRanges[property].push([firstValid[property], lastValid[property]]);
48+
firstValid[property] = null;
49+
}
50+
51+
function doCallback() {
52+
for (var i in keys) {
53+
property = keys[i] + '_' + keyValues[i];
54+
invalidProperty(property);
55+
}
56+
callback(validRanges);
57+
}
58+
}

i18n/ucd/src/generateCode.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
exports.generateCode = generateCode;
3+
exports.generateFunction = generateFunction;
4+
5+
function generateCode(validRanges) {
6+
var code = '/******************************************************\n' +
7+
' * Generated file, do not modify *\n' +
8+
' * *\n' +
9+
' *****************************************************/\n' +
10+
'"use strict";\n';
11+
var keys = Object.keys(validRanges);
12+
for (var i in keys) {
13+
code += generateFunction(validRanges[keys[i]], keys[i]);
14+
}
15+
return code;
16+
}
17+
18+
19+
function generateFunction(positiveElements, functionName) {
20+
var result = [];
21+
result.push('function ', functionName, '(cp) {\n');
22+
positiveElements.forEach(function(range) {
23+
if (range[0] === range[1]) {
24+
result.push(' if (cp === 0x', range[0], ')');
25+
} else {
26+
result.push(' if (0x', range[0], ' <= cp && cp <= 0x', range[1], ')');
27+
}
28+
result.push(' return true;\n');
29+
});
30+
result.push(' return false;\n}\n');
31+
return result.join('');
32+
}
33+

i18n/ucd/src/ucd.all.flat.xml.gz

7.07 MB
Binary file not shown.

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"preinstall": "node scripts/npm/check-node-modules.js --purge",
1818
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
1919
"commit": "git-cz",
20-
"test-i18n": "jasmine-node i18n/spec"
20+
"test-i18n": "jasmine-node i18n/spec",
21+
"test-i18n-ucd": "jasmine-node i18n/ucd/spec"
2122
},
2223
"devDependencies": {
2324
"angular-benchpress": "0.x.x",
@@ -75,6 +76,7 @@
7576
"q-io": "^1.10.9",
7677
"qq": "^0.3.5",
7778
"rewire": "~2.1.0",
79+
"sax": "^1.1.1",
7880
"semver": "~4.0.3",
7981
"shelljs": "~0.3.0",
8082
"sorted-object": "^1.0.0",

src/ngParseExt/module.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
/**
4+
* @ngdoc module
5+
* @name ngParseExt
6+
* @description
7+
*
8+
* # ngParseExt
9+
*
10+
* The `ngParseExt` module provides functionality to allow Unicode characters in
11+
* identifiers inside Angular expressions.
12+
*
13+
*
14+
* <div doc-module-components="ngParseExt"></div>
15+
*
16+
* This module allows the usage of any identifier that follows ES6 identifier naming convention
17+
* to be used as an identifier in an Angular expression. ES6 delegates some of the identifier
18+
* rules definition to Unicode, this module uses ES6 and Unicode 8.0 identifiers convention.
19+
*
20+
*/
21+
22+
/* global angularParseExtModule: true,
23+
IDS_Y,
24+
IDC_Y
25+
*/
26+
27+
function isValidIdentifierStart(ch, cp) {
28+
return ch === '$' ||
29+
ch === '_' ||
30+
IDS_Y(cp);
31+
}
32+
33+
function isValidIdentifierContinue(ch, cp) {
34+
return ch === '$' ||
35+
ch === '_' ||
36+
cp === 0x200C || // <ZWNJ>
37+
cp === 0x200D || // <ZWJ>
38+
IDC_Y(cp);
39+
}
40+
41+
angular.module('ngParseExt', [])
42+
.config(['$parseProvider', function($parseProvider) {
43+
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
44+
}]);

0 commit comments

Comments
 (0)