fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

239
node_modules/gettext-parser/lib/mocompiler.js generated vendored Normal file
View File

@@ -0,0 +1,239 @@
'use strict';
var Buffer = require('safe-buffer').Buffer;
var encoding = require('encoding');
var sharedFuncs = require('./shared');
/**
* Exposes general compiler function. Takes a translation
* object as a parameter and returns binary MO object
*
* @param {Object} table Translation object
* @return {Buffer} Compiled binary MO object
*/
module.exports = function (table) {
var compiler = new Compiler(table);
return compiler.compile();
};
/**
* Creates a MO compiler object.
*
* @constructor
* @param {Object} table Translation table as defined in the README
*/
function Compiler (table) {
this._table = table || {};
this._table.headers = this._table.headers || {};
this._table.translations = this._table.translations || {};
this._translations = [];
this._writeFunc = 'writeUInt32LE';
this._handleCharset();
}
/**
* Magic bytes for the generated binary data
*/
Compiler.prototype.MAGIC = 0x950412de;
/**
* Handles header values, replaces or adds (if needed) a charset property
*/
Compiler.prototype._handleCharset = function () {
var parts = (this._table.headers['content-type'] || 'text/plain').split(';');
var contentType = parts.shift();
var charset = sharedFuncs.formatCharset(this._table.charset);
var params = [];
params = parts.map(function (part) {
var parts = part.split('=');
var key = parts.shift().trim();
var value = parts.join('=');
if (key.toLowerCase() === 'charset') {
if (!charset) {
charset = sharedFuncs.formatCharset(value.trim() || 'utf-8');
}
return 'charset=' + charset;
}
return part;
});
if (!charset) {
charset = this._table.charset || 'utf-8';
params.push('charset=' + charset);
}
this._table.charset = charset;
this._table.headers['content-type'] = contentType + '; ' + params.join('; ');
this._charset = charset;
};
/**
* Generates an array of translation strings
* in the form of [{msgid:... , msgstr:...}]
*
* @return {Array} Translation strings array
*/
Compiler.prototype._generateList = function () {
var list = [];
list.push({
msgid: Buffer.alloc(0),
msgstr: encoding.convert(sharedFuncs.generateHeader(this._table.headers), this._charset)
});
Object.keys(this._table.translations).forEach(function (msgctxt) {
if (typeof this._table.translations[msgctxt] !== 'object') {
return;
}
Object.keys(this._table.translations[msgctxt]).forEach(function (msgid) {
if (typeof this._table.translations[msgctxt][msgid] !== 'object') {
return;
}
if (msgctxt === '' && msgid === '') {
return;
}
var msgidPlural = this._table.translations[msgctxt][msgid].msgid_plural;
var key = msgid;
var value;
if (msgctxt) {
key = msgctxt + '\u0004' + key;
}
if (msgidPlural) {
key += '\u0000' + msgidPlural;
}
value = [].concat(this._table.translations[msgctxt][msgid].msgstr || []).join('\u0000');
list.push({
msgid: encoding.convert(key, this._charset),
msgstr: encoding.convert(value, this._charset)
});
}.bind(this));
}.bind(this));
return list;
};
/**
* Calculate buffer size for the final binary object
*
* @param {Array} list An array of translation strings from _generateList
* @return {Object} Size data of {msgid, msgstr, total}
*/
Compiler.prototype._calculateSize = function (list) {
var msgidLength = 0;
var msgstrLength = 0;
var totalLength = 0;
list.forEach(function (translation) {
msgidLength += translation.msgid.length + 1; // + extra 0x00
msgstrLength += translation.msgstr.length + 1; // + extra 0x00
});
totalLength = 4 + // magic number
4 + // revision
4 + // string count
4 + // original string table offset
4 + // translation string table offset
4 + // hash table size
4 + // hash table offset
(4 + 4) * list.length + // original string table
(4 + 4) * list.length + // translations string table
msgidLength + // originals
msgstrLength; // translations
return {
msgid: msgidLength,
msgstr: msgstrLength,
total: totalLength
};
};
/**
* Generates the binary MO object from the translation list
*
* @param {Array} list translation list
* @param {Object} size Byte size information
* @return {Buffer} Compiled MO object
*/
Compiler.prototype._build = function (list, size) {
var returnBuffer = Buffer.alloc(size.total);
var curPosition = 0;
var i;
var len;
// magic
returnBuffer[this._writeFunc](this.MAGIC, 0);
// revision
returnBuffer[this._writeFunc](0, 4);
// string count
returnBuffer[this._writeFunc](list.length, 8);
// original string table offset
returnBuffer[this._writeFunc](28, 12);
// translation string table offset
returnBuffer[this._writeFunc](28 + (4 + 4) * list.length, 16);
// hash table size
returnBuffer[this._writeFunc](0, 20);
// hash table offset
returnBuffer[this._writeFunc](28 + (4 + 4) * list.length * 2, 24);
// build originals table
curPosition = 28 + 2 * (4 + 4) * list.length;
for (i = 0, len = list.length; i < len; i++) {
list[i].msgid.copy(returnBuffer, curPosition);
returnBuffer[this._writeFunc](list[i].msgid.length, 28 + i * 8);
returnBuffer[this._writeFunc](curPosition, 28 + i * 8 + 4);
returnBuffer[curPosition + list[i].msgid.length] = 0x00;
curPosition += list[i].msgid.length + 1;
}
// build translations table
for (i = 0, len = list.length; i < len; i++) {
list[i].msgstr.copy(returnBuffer, curPosition);
returnBuffer[this._writeFunc](list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
returnBuffer[this._writeFunc](curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
returnBuffer[curPosition + list[i].msgstr.length] = 0x00;
curPosition += list[i].msgstr.length + 1;
}
return returnBuffer;
};
/**
* Compiles translation object into a binary MO object
*
* @return {Buffer} Compiled MO object
*/
Compiler.prototype.compile = function () {
var list = this._generateList();
var size = this._calculateSize(list);
// sort by msgid
list.sort(function (a, b) {
if (a.msgid > b.msgid) {
return 1;
}
if (a.msgid < b.msgid) {
return -1;
}
return 0;
});
return this._build(list, size);
};

204
node_modules/gettext-parser/lib/moparser.js generated vendored Normal file
View File

@@ -0,0 +1,204 @@
'use strict';
var encoding = require('encoding');
var sharedFuncs = require('./shared');
/**
* Parses a binary MO object into translation table
*
* @param {Buffer} buffer Binary MO object
* @param {String} [defaultCharset] Default charset to use
* @return {Object} Translation object
*/
module.exports = function (buffer, defaultCharset) {
var parser = new Parser(buffer, defaultCharset);
return parser.parse();
};
/**
* Creates a MO parser object.
*
* @constructor
* @param {Buffer} fileContents Binary MO object
* @param {String} [defaultCharset] Default charset to use
*/
function Parser (fileContents, defaultCharset) {
this._fileContents = fileContents;
/**
* Method name for writing int32 values, default littleendian
*/
this._writeFunc = 'writeUInt32LE';
/**
* Method name for reading int32 values, default littleendian
*/
this._readFunc = 'readUInt32LE';
this._charset = defaultCharset || 'iso-8859-1';
this._table = {
charset: this._charset,
headers: undefined,
translations: {}
};
}
/**
* Magic constant to check the endianness of the input file
*/
Parser.prototype.MAGIC = 0x950412de;
/**
* Checks if number values in the input file are in big- or littleendian format.
*
* @return {Boolean} Return true if magic was detected
*/
Parser.prototype._checkMagick = function () {
if (this._fileContents.readUInt32LE(0) === this.MAGIC) {
this._readFunc = 'readUInt32LE';
this._writeFunc = 'writeUInt32LE';
return true;
} else if (this._fileContents.readUInt32BE(0) === this.MAGIC) {
this._readFunc = 'readUInt32BE';
this._writeFunc = 'writeUInt32BE';
return true;
} else {
return false;
}
};
/**
* Read the original strings and translations from the input MO file. Use the
* first translation string in the file as the header.
*/
Parser.prototype._loadTranslationTable = function () {
var offsetOriginals = this._offsetOriginals;
var offsetTranslations = this._offsetTranslations;
var position;
var length;
var msgid;
var msgstr;
for (var i = 0; i < this._total; i++) {
// msgid string
length = this._fileContents[this._readFunc](offsetOriginals);
offsetOriginals += 4;
position = this._fileContents[this._readFunc](offsetOriginals);
offsetOriginals += 4;
msgid = this._fileContents.slice(position, position + length);
// matching msgstr
length = this._fileContents[this._readFunc](offsetTranslations);
offsetTranslations += 4;
position = this._fileContents[this._readFunc](offsetTranslations);
offsetTranslations += 4;
msgstr = this._fileContents.slice(position, position + length);
if (!i && !msgid.toString()) {
this._handleCharset(msgstr);
}
msgid = encoding.convert(msgid, 'utf-8', this._charset).toString('utf-8');
msgstr = encoding.convert(msgstr, 'utf-8', this._charset).toString('utf-8');
this._addString(msgid, msgstr);
}
// dump the file contents object
this._fileContents = null;
};
/**
* Detects charset for MO strings from the header
*
* @param {Buffer} headers Header value
*/
Parser.prototype._handleCharset = function (headers) {
var headersStr = headers.toString();
var match;
if ((match = headersStr.match(/[; ]charset\s*=\s*([\w-]+)/i))) {
this._charset = this._table.charset = sharedFuncs.formatCharset(match[1], this._charset);
}
headers = encoding.convert(headers, 'utf-8', this._charset).toString('utf-8');
this._table.headers = sharedFuncs.parseHeader(headers);
};
/**
* Adds a translation to the translation object
*
* @param {String} msgid Original string
* @params {String} msgstr Translation for the original string
*/
Parser.prototype._addString = function (msgid, msgstr) {
var translation = {};
var parts;
var msgctxt;
var msgidPlural;
msgid = msgid.split('\u0004');
if (msgid.length > 1) {
msgctxt = msgid.shift();
translation.msgctxt = msgctxt;
} else {
msgctxt = '';
}
msgid = msgid.join('\u0004');
parts = msgid.split('\u0000');
msgid = parts.shift();
translation.msgid = msgid;
if ((msgidPlural = parts.join('\u0000'))) {
translation.msgid_plural = msgidPlural;
}
msgstr = msgstr.split('\u0000');
translation.msgstr = [].concat(msgstr || []);
if (!this._table.translations[msgctxt]) {
this._table.translations[msgctxt] = {};
}
this._table.translations[msgctxt][msgid] = translation;
};
/**
* Parses the MO object and returns translation table
*
* @return {Object} Translation table
*/
Parser.prototype.parse = function () {
if (!this._checkMagick()) {
return false;
}
/**
* GetText revision nr, usually 0
*/
this._revision = this._fileContents[this._readFunc](4);
/**
* Total count of translated strings
*/
this._total = this._fileContents[this._readFunc](8);
/**
* Offset position for original strings table
*/
this._offsetOriginals = this._fileContents[this._readFunc](12);
/**
* Offset position for translation strings table
*/
this._offsetTranslations = this._fileContents[this._readFunc](16);
// Load translations into this._translationTable
this._loadTranslationTable();
return this._table;
};

240
node_modules/gettext-parser/lib/pocompiler.js generated vendored Normal file
View File

@@ -0,0 +1,240 @@
'use strict';
var Buffer = require('safe-buffer').Buffer;
var encoding = require('encoding');
var sharedFuncs = require('./shared');
/**
* Comparator function for comparing msgid
* @param {Object} object with msgid prev
* @param {Object} object with msgid next
* @returns {number} comparator index
*/
function compare (r1, r2) {
if (r1.msgid > r2.msgid) {
return 1;
}
if (r2.msgid > r1.msgid) {
return -1;
}
return 0;
}
/**
* Exposes general compiler function. Takes a translation
* object as a parameter and returns PO object
*
* @param {Object} table Translation object
* @return {Buffer} Compiled PO object
*/
module.exports = function (table, options) {
var compiler = new Compiler(table, options);
return compiler.compile();
};
/**
* Creates a PO compiler object.
*
* @constructor
* @param {Object} table Translation table to be compiled
*/
function Compiler (table, options) {
this._table = table || {};
this._table.headers = this._table.headers || {};
this._table.translations = this._table.translations || {};
this._options = options || {};
if (!('foldLength' in this._options)) {
this._options.foldLength = 76;
}
if (!('sortByMsgid' in this._options)) {
this._options.sortByMsgid = false;
}
this._translations = [];
this._handleCharset();
}
/**
* Converts a comments object to a comment string. The comment object is
* in the form of {translator:'', reference: '', extracted: '', flag: '', previous:''}
*
* @param {Object} comments A comments object
* @return {String} A comment string for the PO file
*/
Compiler.prototype._drawComments = function (comments) {
var lines = [];
var types = [{
key: 'translator',
prefix: '# '
}, {
key: 'reference',
prefix: '#: '
}, {
key: 'extracted',
prefix: '#. '
}, {
key: 'flag',
prefix: '#, '
}, {
key: 'previous',
prefix: '#| '
}];
types.forEach(function (type) {
if (!comments[type.key]) {
return;
}
comments[type.key].split(/\r?\n|\r/).forEach(function (line) {
lines.push(type.prefix + line);
});
});
return lines.join('\n');
};
/**
* Builds a PO string for a single translation object
*
* @param {Object} block Translation object
* @param {Object} [override] Properties of this object will override `block` properties
* @return {String} Translation string for a single object
*/
Compiler.prototype._drawBlock = function (block, override) {
override = override || {};
var response = [];
var comments = override.comments || block.comments;
var msgctxt = override.msgctxt || block.msgctxt;
var msgid = override.msgid || block.msgid;
var msgidPlural = override.msgid_plural || block.msgid_plural;
var msgstr = [].concat(override.msgstr || block.msgstr);
// add comments
if (comments && (comments = this._drawComments(comments))) {
response.push(comments);
}
if (msgctxt) {
response.push(this._addPOString('msgctxt', msgctxt));
}
response.push(this._addPOString('msgid', msgid || ''));
if (msgidPlural) {
response.push(this._addPOString('msgid_plural', msgidPlural));
msgstr.forEach(function (msgstr, i) {
response.push(this._addPOString('msgstr[' + i + ']', msgstr || ''));
}.bind(this));
} else {
response.push(this._addPOString('msgstr', msgstr[0] || ''));
}
return response.join('\n');
};
/**
* Escapes and joins a key and a value for the PO string
*
* @param {String} key Key name
* @param {String} value Key value
* @return {String} Joined and escaped key-value pair
*/
Compiler.prototype._addPOString = function (key, value) {
key = (key || '').toString();
// escape newlines and quotes
value = (value || '').toString()
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\t/g, '\\t')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');
var lines = [value];
if (this._options.foldLength > 0) {
lines = sharedFuncs.foldLine(value, this._options.foldLength);
}
if (lines.length < 2) {
return key + ' "' + (lines.shift() || '') + '"';
} else {
return key + ' ""\n"' + lines.join('"\n"') + '"';
}
};
/**
* Handles header values, replaces or adds (if needed) a charset property
*/
Compiler.prototype._handleCharset = function () {
var parts = (this._table.headers['content-type'] || 'text/plain').split(';');
var contentType = parts.shift();
var charset = sharedFuncs.formatCharset(this._table.charset);
var params = [];
params = parts.map(function (part) {
var parts = part.split('=');
var key = parts.shift().trim();
var value = parts.join('=');
if (key.toLowerCase() === 'charset') {
if (!charset) {
charset = sharedFuncs.formatCharset(value.trim() || 'utf-8');
}
return 'charset=' + charset;
}
return part;
});
if (!charset) {
charset = this._table.charset || 'utf-8';
params.push('charset=' + charset);
}
this._table.charset = charset;
this._table.headers['content-type'] = contentType + '; ' + params.join('; ');
this._charset = charset;
};
/**
* Compiles translation object into a PO object
*
* @return {Buffer} Compiled PO object
*/
Compiler.prototype.compile = function () {
var response = [];
var headerBlock = (this._table.translations[''] && this._table.translations['']['']) || {};
Object.keys(this._table.translations).forEach(function (msgctxt) {
if (typeof this._table.translations[msgctxt] !== 'object') {
return;
}
Object.keys(this._table.translations[msgctxt]).forEach(function (msgid) {
if (typeof this._table.translations[msgctxt][msgid] !== 'object') {
return;
}
if (msgctxt === '' && msgid === '') {
return;
}
response.push(this._table.translations[msgctxt][msgid]);
}.bind(this));
}.bind(this));
if (this._options.sortByMsgid) {
response = response.sort(compare);
}
response = response.map(function (r) {
return this._drawBlock(r);
}.bind(this));
response.unshift(this._drawBlock(headerBlock, {
msgstr: sharedFuncs.generateHeader(this._table.headers)
}));
if (this._charset === 'utf-8' || this._charset === 'ascii') {
return Buffer.from(response.join('\n\n'), 'utf-8');
} else {
return encoding.convert(response.join('\n\n'), this._charset);
}
};

529
node_modules/gettext-parser/lib/poparser.js generated vendored Normal file
View File

@@ -0,0 +1,529 @@
'use strict';
var encoding = require('encoding');
var sharedFuncs = require('./shared');
var Transform = require('stream').Transform;
var util = require('util');
/**
* Parses a PO object into translation table
*
* @param {Buffer|String} buffer PO object
* @param {String} [defaultCharset] Default charset to use
* @return {Object} Translation object
*/
module.exports.parse = function (buffer, defaultCharset) {
var parser = new Parser(buffer, defaultCharset);
return parser.parse();
};
/**
* Parses a PO stream, emits translation table in object mode
*
* @param {String} [defaultCharset] Default charset to use
* @param {String} [options] Stream options
* @return {Stream} Transform stream
*/
module.exports.stream = function (defaultCharset, options) {
return new PoParserTransform(defaultCharset, options);
};
/**
* Creates a PO parser object. If PO object is a string,
* UTF-8 will be used as the charset
*
* @constructor
* @param {Buffer|String} fileContents PO object
* @param {String} [defaultCharset] Default charset to use
*/
function Parser (fileContents, defaultCharset) {
this._charset = defaultCharset || 'iso-8859-1';
this._lex = [];
this._escaped = false;
this._node = {};
this._state = this.states.none;
if (typeof fileContents === 'string') {
this._charset = 'utf-8';
this._fileContents = fileContents;
} else {
this._handleCharset(fileContents);
}
}
/**
* Parses the PO object and returns translation table
*
* @return {Object} Translation table
*/
Parser.prototype.parse = function () {
this._lexer(this._fileContents);
return this._finalize(this._lex);
};
/**
* Detects charset for PO strings from the header
*
* @param {Buffer} headers Header value
*/
Parser.prototype._handleCharset = function (buf) {
var str = (buf || '').toString();
var pos;
var headers = '';
var match;
if ((pos = str.search(/^\s*msgid/im)) >= 0) {
if ((pos = pos + str.substr(pos + 5).search(/^\s*(msgid|msgctxt)/im))) {
headers = str.substr(0, pos);
}
}
if ((match = headers.match(/[; ]charset\s*=\s*([\w-]+)(?:[\s;]|\\n)*"\s*$/mi))) {
this._charset = sharedFuncs.formatCharset(match[1], this._charset);
}
if (this._charset === 'utf-8') {
this._fileContents = str;
} else {
this._fileContents = this._toString(buf);
}
};
Parser.prototype._toString = function (buf) {
return encoding.convert(buf, 'utf-8', this._charset).toString('utf-8');
};
/**
* State constants for parsing FSM
*/
Parser.prototype.states = {
none: 0x01,
comments: 0x02,
key: 0x03,
string: 0x04
};
/**
* Value types for lexer
*/
Parser.prototype.types = {
comments: 0x01,
key: 0x02,
string: 0x03
};
/**
* String matches for lexer
*/
Parser.prototype.symbols = {
quotes: /["']/,
comments: /#/,
whitespace: /\s/,
key: /[\w\-[\]]/
};
/**
* Token parser. Parsed state can be found from this._lex
*
* @param {String} chunk String
*/
Parser.prototype._lexer = function (chunk) {
var chr;
for (var i = 0, len = chunk.length; i < len; i++) {
chr = chunk.charAt(i);
switch (this._state) {
case this.states.none:
if (chr.match(this.symbols.quotes)) {
this._node = {
type: this.types.string,
value: '',
quote: chr
};
this._lex.push(this._node);
this._state = this.states.string;
} else if (chr.match(this.symbols.comments)) {
this._node = {
type: this.types.comments,
value: ''
};
this._lex.push(this._node);
this._state = this.states.comments;
} else if (!chr.match(this.symbols.whitespace)) {
this._node = {
type: this.types.key,
value: chr
};
this._lex.push(this._node);
this._state = this.states.key;
}
break;
case this.states.comments:
if (chr === '\n') {
this._state = this.states.none;
} else if (chr !== '\r') {
this._node.value += chr;
}
break;
case this.states.string:
if (this._escaped) {
switch (chr) {
case 't':
this._node.value += '\t';
break;
case 'n':
this._node.value += '\n';
break;
case 'r':
this._node.value += '\r';
break;
default:
this._node.value += chr;
}
this._escaped = false;
} else {
if (chr === this._node.quote) {
this._state = this.states.none;
} else if (chr === '\\') {
this._escaped = true;
break;
} else {
this._node.value += chr;
}
this._escaped = false;
}
break;
case this.states.key:
if (!chr.match(this.symbols.key)) {
this._state = this.states.none;
i--;
} else {
this._node.value += chr;
}
break;
}
}
};
/**
* Join multi line strings
*
* @param {Object} tokens Parsed tokens
* @return {Object} Parsed tokens, with multi line strings joined into one
*/
Parser.prototype._joinStringValues = function (tokens) {
var lastNode;
var response = [];
for (var i = 0, len = tokens.length; i < len; i++) {
if (lastNode && tokens[i].type === this.types.string && lastNode.type === this.types.string) {
lastNode.value += tokens[i].value;
} else if (lastNode && tokens[i].type === this.types.comments && lastNode.type === this.types.comments) {
lastNode.value += '\n' + tokens[i].value;
} else {
response.push(tokens[i]);
lastNode = tokens[i];
}
}
return response;
};
/**
* Parse comments into separate comment blocks
*
* @param {Object} tokens Parsed tokens
*/
Parser.prototype._parseComments = function (tokens) {
// parse comments
tokens.forEach(function (node) {
var comment, lines;
if (node && node.type === this.types.comments) {
comment = {
translator: [],
extracted: [],
reference: [],
flag: [],
previous: []
};
lines = (node.value || '').split(/\n/);
lines.forEach(function (line) {
switch (line.charAt(0) || '') {
case ':':
comment.reference.push(line.substr(1).trim());
break;
case '.':
comment.extracted.push(line.substr(1).replace(/^\s+/, ''));
break;
case ',':
comment.flag.push(line.substr(1).replace(/^\s+/, ''));
break;
case '|':
comment.previous.push(line.substr(1).replace(/^\s+/, ''));
break;
default:
comment.translator.push(line.replace(/^\s+/, ''));
}
});
node.value = {};
Object.keys(comment).forEach(function (key) {
if (comment[key] && comment[key].length) {
node.value[key] = comment[key].join('\n');
}
});
}
}.bind(this));
};
/**
* Join gettext keys with values
*
* @param {Object} tokens Parsed tokens
* @return {Object} Tokens
*/
Parser.prototype._handleKeys = function (tokens) {
var response = [];
var lastNode;
for (var i = 0, len = tokens.length; i < len; i++) {
if (tokens[i].type === this.types.key) {
lastNode = {
key: tokens[i].value
};
if (i && tokens[i - 1].type === this.types.comments) {
lastNode.comments = tokens[i - 1].value;
}
lastNode.value = '';
response.push(lastNode);
} else if (tokens[i].type === this.types.string && lastNode) {
lastNode.value += tokens[i].value;
}
}
return response;
};
/**
* Separate different values into individual translation objects
*
* @param {Object} tokens Parsed tokens
* @return {Object} Tokens
*/
Parser.prototype._handleValues = function (tokens) {
var response = [];
var lastNode;
var curContext;
var curComments;
for (var i = 0, len = tokens.length; i < len; i++) {
if (tokens[i].key.toLowerCase() === 'msgctxt') {
curContext = tokens[i].value;
curComments = tokens[i].comments;
} else if (tokens[i].key.toLowerCase() === 'msgid') {
lastNode = {
msgid: tokens[i].value
};
if (curContext) {
lastNode.msgctxt = curContext;
}
if (curComments) {
lastNode.comments = curComments;
}
if (tokens[i].comments && !lastNode.comments) {
lastNode.comments = tokens[i].comments;
}
curContext = false;
curComments = false;
response.push(lastNode);
} else if (tokens[i].key.toLowerCase() === 'msgid_plural') {
if (lastNode) {
lastNode.msgid_plural = tokens[i].value;
}
if (tokens[i].comments && !lastNode.comments) {
lastNode.comments = tokens[i].comments;
}
curContext = false;
curComments = false;
} else if (tokens[i].key.substr(0, 6).toLowerCase() === 'msgstr') {
if (lastNode) {
lastNode.msgstr = (lastNode.msgstr || []).concat(tokens[i].value);
}
if (tokens[i].comments && !lastNode.comments) {
lastNode.comments = tokens[i].comments;
}
curContext = false;
curComments = false;
}
}
return response;
};
/**
* Compose a translation table from tokens object
*
* @param {Object} tokens Parsed tokens
* @return {Object} Translation table
*/
Parser.prototype._normalize = function (tokens) {
var msgctxt;
var table = {
charset: this._charset,
headers: undefined,
translations: {}
};
for (var i = 0, len = tokens.length; i < len; i++) {
msgctxt = tokens[i].msgctxt || '';
if (!table.translations[msgctxt]) {
table.translations[msgctxt] = {};
}
if (!table.headers && !msgctxt && !tokens[i].msgid) {
table.headers = sharedFuncs.parseHeader(tokens[i].msgstr[0]);
}
table.translations[msgctxt][tokens[i].msgid] = tokens[i];
}
return table;
};
/**
* Converts parsed tokens to a translation table
*
* @param {Object} tokens Parsed tokens
* @returns {Object} Translation table
*/
Parser.prototype._finalize = function (tokens) {
var data = this._joinStringValues(tokens);
this._parseComments(data);
data = this._handleKeys(data);
data = this._handleValues(data);
return this._normalize(data);
};
/**
* Creates a transform stream for parsing PO input
*
* @constructor
* @param {String} [defaultCharset] Default charset to use
* @param {String} [options] Stream options
*/
function PoParserTransform (defaultCharset, options) {
if (!options && defaultCharset && typeof defaultCharset === 'object') {
options = defaultCharset;
defaultCharset = undefined;
}
this.defaultCharset = defaultCharset;
this._parser = false;
this._tokens = {};
this._cache = [];
this._cacheSize = 0;
this.initialTreshold = options.initialTreshold || 2 * 1024;
Transform.call(this, options);
this._writableState.objectMode = false;
this._readableState.objectMode = true;
}
util.inherits(PoParserTransform, Transform);
/**
* Processes a chunk of the input stream
*/
PoParserTransform.prototype._transform = function (chunk, encoding, done) {
var i;
var len = 0;
if (!chunk || !chunk.length) {
return done();
}
if (!this._parser) {
this._cache.push(chunk);
this._cacheSize += chunk.length;
// wait until the first 1kb before parsing headers for charset
if (this._cacheSize < this.initialTreshold) {
return setImmediate(done);
} else if (this._cacheSize) {
chunk = Buffer.concat(this._cache, this._cacheSize);
this._cacheSize = 0;
this._cache = [];
}
this._parser = new Parser(chunk, this.defaultCharset);
} else if (this._cacheSize) {
// this only happens if we had an uncompleted 8bit sequence from the last iteration
this._cache.push(chunk);
this._cacheSize += chunk.length;
chunk = Buffer.concat(this._cache, this._cacheSize);
this._cacheSize = 0;
this._cache = [];
}
// cache 8bit bytes from the end of the chunk
// helps if the chunk ends in the middle of an utf-8 sequence
for (i = chunk.length - 1; i >= 0; i--) {
if (chunk[i] >= 0x80) {
len++;
continue;
}
break;
}
// it seems we found some 8bit bytes from the end of the string, so let's cache these
if (len) {
this._cache = [chunk.slice(chunk.length - len)];
this._cacheSize = this._cache[0].length;
chunk = chunk.slice(0, chunk.length - len);
}
// chunk might be empty if it only continued of 8bit bytes and these were all cached
if (chunk.length) {
this._parser._lexer(this._parser._toString(chunk));
}
setImmediate(done);
};
/**
* Once all input has been processed emit the parsed translation table as an object
*/
PoParserTransform.prototype._flush = function (done) {
var chunk;
if (this._cacheSize) {
chunk = Buffer.concat(this._cache, this._cacheSize);
}
if (!this._parser && chunk) {
this._parser = new Parser(chunk, this.defaultCharset);
}
if (chunk) {
this._parser._lexer(this._parser._toString(chunk));
}
if (this._parser) {
this.push(this._parser._finalize(this._parser._lex));
}
setImmediate(done);
};

126
node_modules/gettext-parser/lib/shared.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
'use strict';
// Expose to the world
module.exports.parseHeader = parseHeader;
module.exports.generateHeader = generateHeader;
module.exports.formatCharset = formatCharset;
module.exports.foldLine = foldLine;
/**
* Parses a header string into an object of key-value pairs
*
* @param {String} str Header string
* @return {Object} An object of key-value pairs
*/
function parseHeader (str) {
var lines = (str || '').split('\n');
var headers = {};
lines.forEach(function (line) {
var parts = line.trim().split(':');
var key = (parts.shift() || '').trim().toLowerCase();
var value = parts.join(':').trim();
if (!key) {
return;
}
headers[key] = value;
});
return headers;
}
/**
* Convert first letters after - to uppercase, other lowercase
*
* @param {String} str String to be updated
* @return {String} A string with uppercase words
*/
function upperCaseWords (str) {
return (str || '')
.toLowerCase()
.trim()
.replace(/^(MIME|POT?(?=-)|[a-z])|-[a-z]/gi, function (str) {
return str.toUpperCase();
});
}
/**
* Joins a header object of key value pairs into a header string
*
* @param {Object} header Object of key value pairs
* @return {String} Header string
*/
function generateHeader (header) {
var lines = [];
Object.keys(header || {}).forEach(function (key) {
if (key) {
lines.push(upperCaseWords(key) + ': ' + (header[key] || '').trim());
}
});
return lines.join('\n') + (lines.length ? '\n' : '');
}
/**
* Normalizes charset name. Converts utf8 to utf-8, WIN1257 to windows-1257 etc.
*
* @param {String} charset Charset name
* @return {String} Normalized charset name
*/
function formatCharset (charset, defaultCharset) {
return (charset || 'iso-8859-1').toString().toLowerCase()
.replace(/^utf[-_]?(\d+)$/, 'utf-$1')
.replace(/^win(?:dows)?[-_]?(\d+)$/, 'windows-$1')
.replace(/^latin[-_]?(\d+)$/, 'iso-8859-$1')
.replace(/^(us[-_]?)?ascii$/, 'ascii')
.replace(/^charset$/, defaultCharset || 'iso-8859-1')
.trim();
}
/**
* Folds long lines according to PO format
*
* @param {String} str PO formatted string to be folded
* @param {Number} [maxLen=76] Maximum allowed length for folded lines
* @return {Array} An array of lines
*/
function foldLine (str, maxLen) {
maxLen = maxLen || 76;
var lines = [];
var curLine = '';
var pos = 0;
var len = str.length;
var match;
while (pos < len) {
curLine = str.substr(pos, maxLen);
// ensure that the line never ends with a partial escaping
// make longer lines if needed
while (curLine.substr(-1) === '\\' && pos + curLine.length < len) {
curLine += str.charAt(pos + curLine.length);
}
// ensure that if possible, line breaks are done at reasonable places
if ((match = /.*?\\n/.exec(curLine))) {
// use everything before and including the first line break
curLine = match[0];
} else if (pos + curLine.length < len) {
// if we're not at the end
if ((match = /.*\s+/.exec(curLine)) && /[^\s]/.test(match[0])) {
// use everything before and including the last white space character (if anything)
curLine = match[0];
} else if ((match = /.*[\x21-\x2f0-9\x5b-\x60\x7b-\x7e]+/.exec(curLine)) && /[^\x21-\x2f0-9\x5b-\x60\x7b-\x7e]/.test(match[0])) {
// use everything before and including the last "special" character (if anything)
curLine = match[0];
}
}
lines.push(curLine);
pos += curLine.length;
}
return lines;
}