mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Merge pull request #1248 from NativeScript/hdeshev/require-maps
webpack improvements
This commit is contained in:
@@ -442,10 +442,17 @@ export module knownFolders {
|
||||
|
||||
export var currentApp = function (): Folder {
|
||||
if (!_app) {
|
||||
var currentDir = __dirname;
|
||||
var path = currentDir.substring(0, currentDir.indexOf("/tns_modules"));
|
||||
const currentDir = __dirname;
|
||||
const tnsModulesIndex = currentDir.indexOf("/tns_modules");
|
||||
|
||||
// Module not hosted in ~/tns_modules when bundled. Use current dir.
|
||||
let appPath = currentDir;
|
||||
if (tnsModulesIndex !== -1) {
|
||||
// Strip part after tns_modules to obtain app root
|
||||
appPath = currentDir.substring(0, tnsModulesIndex);
|
||||
}
|
||||
_app = new Folder();
|
||||
_app[pathProperty] = path;
|
||||
_app[pathProperty] = appPath;
|
||||
_app[isKnownProperty] = true;
|
||||
}
|
||||
|
||||
@@ -465,4 +472,4 @@ export module path {
|
||||
}
|
||||
|
||||
export var separator = getFileAccess().getPathSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,36 @@ global.moduleMerge = function (sourceExports: any, destExports: any) {
|
||||
}
|
||||
}
|
||||
|
||||
type ModuleLoader = () => any;
|
||||
const modules: Map<string, ModuleLoader> = new Map<string, ModuleLoader>();
|
||||
|
||||
global.registerModule = function(name: string, loader: ModuleLoader): void {
|
||||
modules.set(name, loader);
|
||||
}
|
||||
|
||||
global.moduleExists = function(name: string): boolean {
|
||||
return modules.has(name);
|
||||
}
|
||||
|
||||
global.loadModule = function(name: string): any {
|
||||
const loader = modules.get(name);
|
||||
if (loader) {
|
||||
return loader();
|
||||
} else {
|
||||
return require(name);
|
||||
}
|
||||
}
|
||||
|
||||
global.registerModule("timer", () => require("timer"));
|
||||
global.registerModule("ui/dialogs", () => require("ui/dialogs"));
|
||||
global.registerModule("../xhr/xhr", () => require("../xhr/xhr"));
|
||||
global.registerModule("fetch", () => require("fetch"));
|
||||
|
||||
function registerOnGlobalContext(name, module) {
|
||||
Object.defineProperty(global, name, {
|
||||
get: function () {
|
||||
// We do not need to cache require() call since it is already cached in the runtime.
|
||||
let m = require(module);
|
||||
let m = global.loadModule(module);
|
||||
global.moduleMerge(m, global);
|
||||
|
||||
// Redefine the property to make sure the above code is executed only once.
|
||||
|
||||
@@ -7,403 +7,387 @@
|
||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
(function (global) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
function detectEval() {
|
||||
// Don't test for eval if we're running in a Chrome App environment.
|
||||
// We check for APIs set that only exist in a Chrome App context.
|
||||
if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Firefox OS Apps do not allow eval. This feature detection is very hacky
|
||||
// but even if some other platform adds support for this function this code
|
||||
// will continue to work.
|
||||
if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var f = new Function('', 'return true;');
|
||||
return f();
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
function detectEval() {
|
||||
// Don't test for eval if we're running in a Chrome App environment.
|
||||
// We check for APIs set that only exist in a Chrome App context.
|
||||
if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasEval = detectEval();
|
||||
|
||||
function isIndex(s) {
|
||||
return +s === s >>> 0 && s !== '';
|
||||
// Firefox OS Apps do not allow eval. This feature detection is very hacky
|
||||
// but even if some other platform adds support for this function this code
|
||||
// will continue to work.
|
||||
if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function toNumber(s) {
|
||||
return +s;
|
||||
try {
|
||||
var f = new Function('', 'return true;');
|
||||
return f();
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isObject(obj) {
|
||||
return obj === Object(obj);
|
||||
}
|
||||
var hasEval = detectEval();
|
||||
|
||||
var numberIsNaN = global.Number.isNaN || function (value) {
|
||||
return typeof value === 'number' && global.isNaN(value);
|
||||
}
|
||||
function isIndex(s) {
|
||||
return +s === s >>> 0 && s !== '';
|
||||
}
|
||||
|
||||
function areSameValue(left, right) {
|
||||
if (left === right)
|
||||
return left !== 0 || 1 / left === 1 / right;
|
||||
if (numberIsNaN(left) && numberIsNaN(right))
|
||||
return true;
|
||||
function toNumber(s) {
|
||||
return +s;
|
||||
}
|
||||
|
||||
return left !== left && right !== right;
|
||||
}
|
||||
function isObject(obj) {
|
||||
return obj === Object(obj);
|
||||
}
|
||||
|
||||
var createObject = ('__proto__' in {}) ?
|
||||
function (obj) { return obj; } :
|
||||
function (obj) {
|
||||
var proto = obj.__proto__;
|
||||
if (!proto)
|
||||
return obj;
|
||||
var newObject = Object.create(proto);
|
||||
Object.getOwnPropertyNames(obj).forEach(function (name) {
|
||||
Object.defineProperty(newObject, name,
|
||||
Object.getOwnPropertyDescriptor(obj, name));
|
||||
});
|
||||
return newObject;
|
||||
};
|
||||
var numberIsNaN = Number.isNaN || function (value) {
|
||||
return typeof value === 'number' && isNaN(value);
|
||||
}
|
||||
|
||||
var identStart = '[\$_a-zA-Z]';
|
||||
var identPart = '[\$_a-zA-Z0-9]';
|
||||
var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
|
||||
function areSameValue(left, right) {
|
||||
if (left === right)
|
||||
return left !== 0 || 1 / left === 1 / right;
|
||||
if (numberIsNaN(left) && numberIsNaN(right))
|
||||
return true;
|
||||
|
||||
function getPathCharType(char) {
|
||||
if (char === undefined)
|
||||
return 'eof';
|
||||
return left !== left && right !== right;
|
||||
}
|
||||
|
||||
var code = char.charCodeAt(0);
|
||||
var createObject = ('__proto__' in {}) ?
|
||||
function (obj) { return obj; } :
|
||||
function (obj) {
|
||||
var proto = obj.__proto__;
|
||||
if (!proto)
|
||||
return obj;
|
||||
var newObject = Object.create(proto);
|
||||
Object.getOwnPropertyNames(obj).forEach(function (name) {
|
||||
Object.defineProperty(newObject, name,
|
||||
Object.getOwnPropertyDescriptor(obj, name));
|
||||
});
|
||||
return newObject;
|
||||
};
|
||||
|
||||
switch (code) {
|
||||
case 0x5B: // [
|
||||
case 0x5D: // ]
|
||||
case 0x2E: // .
|
||||
case 0x22: // "
|
||||
case 0x27: // '
|
||||
case 0x30: // 0
|
||||
return char;
|
||||
var identStart = '[\$_a-zA-Z]';
|
||||
var identPart = '[\$_a-zA-Z0-9]';
|
||||
var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
|
||||
|
||||
case 0x5F: // _
|
||||
case 0x24: // $
|
||||
return 'ident';
|
||||
function getPathCharType(char) {
|
||||
if (char === undefined)
|
||||
return 'eof';
|
||||
|
||||
case 0x20: // Space
|
||||
case 0x09: // Tab
|
||||
case 0x0A: // Newline
|
||||
case 0x0D: // Return
|
||||
case 0xA0: // No-break space
|
||||
case 0xFEFF: // Byte Order Mark
|
||||
case 0x2028: // Line Separator
|
||||
case 0x2029: // Paragraph Separator
|
||||
return 'ws';
|
||||
}
|
||||
var code = char.charCodeAt(0);
|
||||
|
||||
// a-z, A-Z
|
||||
if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
|
||||
switch (code) {
|
||||
case 0x5B: // [
|
||||
case 0x5D: // ]
|
||||
case 0x2E: // .
|
||||
case 0x22: // "
|
||||
case 0x27: // '
|
||||
case 0x30: // 0
|
||||
return char;
|
||||
|
||||
case 0x5F: // _
|
||||
case 0x24: // $
|
||||
return 'ident';
|
||||
|
||||
// 1-9
|
||||
if (0x31 <= code && code <= 0x39)
|
||||
return 'number';
|
||||
|
||||
return 'else';
|
||||
case 0x20: // Space
|
||||
case 0x09: // Tab
|
||||
case 0x0A: // Newline
|
||||
case 0x0D: // Return
|
||||
case 0xA0: // No-break space
|
||||
case 0xFEFF: // Byte Order Mark
|
||||
case 0x2028: // Line Separator
|
||||
case 0x2029: // Paragraph Separator
|
||||
return 'ws';
|
||||
}
|
||||
|
||||
var pathStateMachine = {
|
||||
'beforePath': {
|
||||
'ws': ['beforePath'],
|
||||
'ident': ['inIdent', 'append'],
|
||||
'[': ['beforeElement'],
|
||||
'eof': ['afterPath']
|
||||
},
|
||||
// a-z, A-Z
|
||||
if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
|
||||
return 'ident';
|
||||
|
||||
'inPath': {
|
||||
'ws': ['inPath'],
|
||||
'.': ['beforeIdent'],
|
||||
'[': ['beforeElement'],
|
||||
'eof': ['afterPath']
|
||||
},
|
||||
// 1-9
|
||||
if (0x31 <= code && code <= 0x39)
|
||||
return 'number';
|
||||
|
||||
'beforeIdent': {
|
||||
'ws': ['beforeIdent'],
|
||||
'ident': ['inIdent', 'append']
|
||||
},
|
||||
return 'else';
|
||||
}
|
||||
|
||||
'inIdent': {
|
||||
'ident': ['inIdent', 'append'],
|
||||
'0': ['inIdent', 'append'],
|
||||
'number': ['inIdent', 'append'],
|
||||
'ws': ['inPath', 'push'],
|
||||
'.': ['beforeIdent', 'push'],
|
||||
'[': ['beforeElement', 'push'],
|
||||
'eof': ['afterPath', 'push']
|
||||
},
|
||||
var pathStateMachine = {
|
||||
'beforePath': {
|
||||
'ws': ['beforePath'],
|
||||
'ident': ['inIdent', 'append'],
|
||||
'[': ['beforeElement'],
|
||||
'eof': ['afterPath']
|
||||
},
|
||||
|
||||
'beforeElement': {
|
||||
'ws': ['beforeElement'],
|
||||
'0': ['afterZero', 'append'],
|
||||
'number': ['inIndex', 'append'],
|
||||
"'": ['inSingleQuote', 'append', ''],
|
||||
'"': ['inDoubleQuote', 'append', '']
|
||||
},
|
||||
'inPath': {
|
||||
'ws': ['inPath'],
|
||||
'.': ['beforeIdent'],
|
||||
'[': ['beforeElement'],
|
||||
'eof': ['afterPath']
|
||||
},
|
||||
|
||||
'afterZero': {
|
||||
'ws': ['afterElement', 'push'],
|
||||
']': ['inPath', 'push']
|
||||
},
|
||||
'beforeIdent': {
|
||||
'ws': ['beforeIdent'],
|
||||
'ident': ['inIdent', 'append']
|
||||
},
|
||||
|
||||
'inIndex': {
|
||||
'0': ['inIndex', 'append'],
|
||||
'number': ['inIndex', 'append'],
|
||||
'ws': ['afterElement'],
|
||||
']': ['inPath', 'push']
|
||||
},
|
||||
'inIdent': {
|
||||
'ident': ['inIdent', 'append'],
|
||||
'0': ['inIdent', 'append'],
|
||||
'number': ['inIdent', 'append'],
|
||||
'ws': ['inPath', 'push'],
|
||||
'.': ['beforeIdent', 'push'],
|
||||
'[': ['beforeElement', 'push'],
|
||||
'eof': ['afterPath', 'push']
|
||||
},
|
||||
|
||||
'inSingleQuote': {
|
||||
"'": ['afterElement'],
|
||||
'eof': ['error'],
|
||||
'else': ['inSingleQuote', 'append']
|
||||
},
|
||||
'beforeElement': {
|
||||
'ws': ['beforeElement'],
|
||||
'0': ['afterZero', 'append'],
|
||||
'number': ['inIndex', 'append'],
|
||||
"'": ['inSingleQuote', 'append', ''],
|
||||
'"': ['inDoubleQuote', 'append', '']
|
||||
},
|
||||
|
||||
'inDoubleQuote': {
|
||||
'"': ['afterElement'],
|
||||
'eof': ['error'],
|
||||
'else': ['inDoubleQuote', 'append']
|
||||
},
|
||||
'afterZero': {
|
||||
'ws': ['afterElement', 'push'],
|
||||
']': ['inPath', 'push']
|
||||
},
|
||||
|
||||
'afterElement': {
|
||||
'ws': ['afterElement'],
|
||||
']': ['inPath', 'push']
|
||||
}
|
||||
'inIndex': {
|
||||
'0': ['inIndex', 'append'],
|
||||
'number': ['inIndex', 'append'],
|
||||
'ws': ['afterElement'],
|
||||
']': ['inPath', 'push']
|
||||
},
|
||||
|
||||
'inSingleQuote': {
|
||||
"'": ['afterElement'],
|
||||
'eof': ['error'],
|
||||
'else': ['inSingleQuote', 'append']
|
||||
},
|
||||
|
||||
'inDoubleQuote': {
|
||||
'"': ['afterElement'],
|
||||
'eof': ['error'],
|
||||
'else': ['inDoubleQuote', 'append']
|
||||
},
|
||||
|
||||
'afterElement': {
|
||||
'ws': ['afterElement'],
|
||||
']': ['inPath', 'push']
|
||||
}
|
||||
}
|
||||
|
||||
function noop() { }
|
||||
function noop() { }
|
||||
|
||||
function parsePath(path) {
|
||||
var keys = [];
|
||||
var index = -1;
|
||||
var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
|
||||
function parsePath(path) {
|
||||
var keys = [];
|
||||
var index = -1;
|
||||
var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
|
||||
|
||||
var actions = {
|
||||
push: function () {
|
||||
if (key === undefined)
|
||||
return;
|
||||
|
||||
keys.push(key);
|
||||
key = undefined;
|
||||
},
|
||||
|
||||
append: function () {
|
||||
if (key === undefined)
|
||||
key = newChar
|
||||
else
|
||||
key += newChar;
|
||||
}
|
||||
};
|
||||
|
||||
function maybeUnescapeQuote() {
|
||||
if (index >= path.length)
|
||||
var actions = {
|
||||
push: function () {
|
||||
if (key === undefined)
|
||||
return;
|
||||
|
||||
var nextChar = path[index + 1];
|
||||
if ((mode == 'inSingleQuote' && nextChar == "'") ||
|
||||
(mode == 'inDoubleQuote' && nextChar == '"')) {
|
||||
index++;
|
||||
newChar = nextChar;
|
||||
actions.append();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
keys.push(key);
|
||||
key = undefined;
|
||||
},
|
||||
|
||||
while (mode) {
|
||||
append: function () {
|
||||
if (key === undefined)
|
||||
key = newChar
|
||||
else
|
||||
key += newChar;
|
||||
}
|
||||
};
|
||||
|
||||
function maybeUnescapeQuote() {
|
||||
if (index >= path.length)
|
||||
return;
|
||||
|
||||
var nextChar = path[index + 1];
|
||||
if ((mode == 'inSingleQuote' && nextChar == "'") ||
|
||||
(mode == 'inDoubleQuote' && nextChar == '"')) {
|
||||
index++;
|
||||
c = path[index];
|
||||
|
||||
if (c == '\\' && maybeUnescapeQuote(mode))
|
||||
continue;
|
||||
|
||||
type = getPathCharType(c);
|
||||
typeMap = pathStateMachine[mode];
|
||||
transition = typeMap[type] || typeMap['else'] || 'error';
|
||||
|
||||
if (transition == 'error')
|
||||
return; // parse error;
|
||||
|
||||
mode = transition[0];
|
||||
action = actions[transition[1]] || noop;
|
||||
newChar = transition[2] === undefined ? c : transition[2];
|
||||
action();
|
||||
|
||||
if (mode === 'afterPath') {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
return; // parse error
|
||||
}
|
||||
|
||||
function isIdent(s) {
|
||||
return identRegExp.test(s);
|
||||
}
|
||||
|
||||
var constructorIsPrivate = {};
|
||||
|
||||
function Path(parts, privateToken) {
|
||||
if (privateToken !== constructorIsPrivate)
|
||||
throw Error('Use Path.get to retrieve path objects');
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
this.push(String(parts[i]));
|
||||
}
|
||||
|
||||
if (hasEval && this.length) {
|
||||
this.getValueFrom = this.compiledGetValueFromFn();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rafaelw): Make simple LRU cache
|
||||
var pathCache = {};
|
||||
|
||||
function getPath(pathString) {
|
||||
if (pathString instanceof Path)
|
||||
return pathString;
|
||||
|
||||
if (pathString == null || pathString.length == 0)
|
||||
pathString = '';
|
||||
|
||||
if (typeof pathString != 'string') {
|
||||
if (isIndex(pathString.length)) {
|
||||
// Constructed with array-like (pre-parsed) keys
|
||||
return new Path(pathString, constructorIsPrivate);
|
||||
}
|
||||
|
||||
pathString = String(pathString);
|
||||
}
|
||||
|
||||
var path = pathCache[pathString];
|
||||
if (path)
|
||||
return path;
|
||||
|
||||
var parts = parsePath(pathString);
|
||||
if (!parts)
|
||||
return invalidPath;
|
||||
|
||||
var path = new Path(parts, constructorIsPrivate);
|
||||
pathCache[pathString] = path;
|
||||
return path;
|
||||
}
|
||||
|
||||
Path.get = getPath;
|
||||
|
||||
function formatAccessor(key) {
|
||||
if (isIndex(key)) {
|
||||
return '[' + key + ']';
|
||||
} else {
|
||||
return '["' + key.replace(/"/g, '\\"') + '"]';
|
||||
}
|
||||
}
|
||||
|
||||
Path.prototype = createObject({
|
||||
__proto__: [],
|
||||
valid: true,
|
||||
|
||||
toString: function () {
|
||||
var pathString = '';
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var key = this[i];
|
||||
if (isIdent(key)) {
|
||||
pathString += i ? '.' + key : key;
|
||||
} else {
|
||||
pathString += formatAccessor(key);
|
||||
}
|
||||
}
|
||||
|
||||
return pathString;
|
||||
},
|
||||
|
||||
getValueFrom: function (obj, directObserver) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (obj == null)
|
||||
return;
|
||||
obj = obj[this[i]];
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
iterateObjects: function (obj, observe) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i)
|
||||
obj = obj[this[i - 1]];
|
||||
if (!isObject(obj))
|
||||
return;
|
||||
observe(obj, this[i]);
|
||||
}
|
||||
},
|
||||
|
||||
compiledGetValueFromFn: function () {
|
||||
var str = '';
|
||||
var pathString = 'obj';
|
||||
str += 'if (obj != null';
|
||||
var i = 0;
|
||||
var key;
|
||||
for (; i < (this.length - 1) ; i++) {
|
||||
key = this[i];
|
||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
||||
str += ' &&\n ' + pathString + ' != null';
|
||||
}
|
||||
str += ')\n';
|
||||
|
||||
var key = this[i];
|
||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
||||
|
||||
str += ' return ' + pathString + ';\nelse\n return undefined;';
|
||||
return new Function('obj', str);
|
||||
},
|
||||
|
||||
setValueFrom: function (obj, value) {
|
||||
if (!this.length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < this.length - 1; i++) {
|
||||
if (!isObject(obj))
|
||||
return false;
|
||||
obj = obj[this[i]];
|
||||
}
|
||||
|
||||
if (!isObject(obj))
|
||||
return false;
|
||||
|
||||
obj[this[i]] = value;
|
||||
newChar = nextChar;
|
||||
actions.append();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var invalidPath = new Path('', constructorIsPrivate);
|
||||
invalidPath.valid = false;
|
||||
invalidPath.getValueFrom = invalidPath.setValueFrom = function () { };
|
||||
|
||||
// Export the observe-js object for **Node.js**, with
|
||||
// backwards-compatibility for the old `require()` API. If we're in
|
||||
// the browser, export as a global object.
|
||||
|
||||
var expose = global;
|
||||
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
expose = exports = module.exports;
|
||||
}
|
||||
expose = exports;
|
||||
}
|
||||
|
||||
expose.Path = Path;
|
||||
while (mode) {
|
||||
index++;
|
||||
c = path[index];
|
||||
|
||||
})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
|
||||
if (c == '\\' && maybeUnescapeQuote(mode))
|
||||
continue;
|
||||
|
||||
type = getPathCharType(c);
|
||||
typeMap = pathStateMachine[mode];
|
||||
transition = typeMap[type] || typeMap['else'] || 'error';
|
||||
|
||||
if (transition == 'error')
|
||||
return; // parse error;
|
||||
|
||||
mode = transition[0];
|
||||
action = actions[transition[1]] || noop;
|
||||
newChar = transition[2] === undefined ? c : transition[2];
|
||||
action();
|
||||
|
||||
if (mode === 'afterPath') {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
return; // parse error
|
||||
}
|
||||
|
||||
function isIdent(s) {
|
||||
return identRegExp.test(s);
|
||||
}
|
||||
|
||||
var constructorIsPrivate = {};
|
||||
|
||||
function Path(parts, privateToken) {
|
||||
if (privateToken !== constructorIsPrivate)
|
||||
throw Error('Use Path.get to retrieve path objects');
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
this.push(String(parts[i]));
|
||||
}
|
||||
|
||||
if (hasEval && this.length) {
|
||||
this.getValueFrom = this.compiledGetValueFromFn();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rafaelw): Make simple LRU cache
|
||||
var pathCache = {};
|
||||
|
||||
function getPath(pathString) {
|
||||
if (pathString instanceof Path)
|
||||
return pathString;
|
||||
|
||||
if (pathString == null || pathString.length == 0)
|
||||
pathString = '';
|
||||
|
||||
if (typeof pathString != 'string') {
|
||||
if (isIndex(pathString.length)) {
|
||||
// Constructed with array-like (pre-parsed) keys
|
||||
return new Path(pathString, constructorIsPrivate);
|
||||
}
|
||||
|
||||
pathString = String(pathString);
|
||||
}
|
||||
|
||||
var path = pathCache[pathString];
|
||||
if (path)
|
||||
return path;
|
||||
|
||||
var parts = parsePath(pathString);
|
||||
if (!parts)
|
||||
return invalidPath;
|
||||
|
||||
var path = new Path(parts, constructorIsPrivate);
|
||||
pathCache[pathString] = path;
|
||||
return path;
|
||||
}
|
||||
|
||||
Path.get = getPath;
|
||||
|
||||
function formatAccessor(key) {
|
||||
if (isIndex(key)) {
|
||||
return '[' + key + ']';
|
||||
} else {
|
||||
return '["' + key.replace(/"/g, '\\"') + '"]';
|
||||
}
|
||||
}
|
||||
|
||||
Path.prototype = createObject({
|
||||
__proto__: [],
|
||||
valid: true,
|
||||
|
||||
toString: function () {
|
||||
var pathString = '';
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var key = this[i];
|
||||
if (isIdent(key)) {
|
||||
pathString += i ? '.' + key : key;
|
||||
} else {
|
||||
pathString += formatAccessor(key);
|
||||
}
|
||||
}
|
||||
|
||||
return pathString;
|
||||
},
|
||||
|
||||
getValueFrom: function (obj, directObserver) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (obj == null)
|
||||
return;
|
||||
obj = obj[this[i]];
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
iterateObjects: function (obj, observe) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i)
|
||||
obj = obj[this[i - 1]];
|
||||
if (!isObject(obj))
|
||||
return;
|
||||
observe(obj, this[i]);
|
||||
}
|
||||
},
|
||||
|
||||
compiledGetValueFromFn: function () {
|
||||
var str = '';
|
||||
var pathString = 'obj';
|
||||
str += 'if (obj != null';
|
||||
var i = 0;
|
||||
var key;
|
||||
for (; i < (this.length - 1) ; i++) {
|
||||
key = this[i];
|
||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
||||
str += ' &&\n ' + pathString + ' != null';
|
||||
}
|
||||
str += ')\n';
|
||||
|
||||
var key = this[i];
|
||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
||||
|
||||
str += ' return ' + pathString + ';\nelse\n return undefined;';
|
||||
return new Function('obj', str);
|
||||
},
|
||||
|
||||
setValueFrom: function (obj, value) {
|
||||
if (!this.length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < this.length - 1; i++) {
|
||||
if (!isObject(obj))
|
||||
return false;
|
||||
obj = obj[this[i]];
|
||||
}
|
||||
|
||||
if (!isObject(obj))
|
||||
return false;
|
||||
|
||||
obj[this[i]] = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var invalidPath = new Path('', constructorIsPrivate);
|
||||
invalidPath.valid = false;
|
||||
invalidPath.getValueFrom = invalidPath.setValueFrom = function () { };
|
||||
|
||||
exports.Path = Path;
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
import view = require("ui/core/view");
|
||||
import fs = require("file-system");
|
||||
import xml = require("xml");
|
||||
import types = require("utils/types");
|
||||
import componentBuilder = require("ui/builder/component-builder");
|
||||
import platform = require("platform");
|
||||
import definition = require("ui/builder");
|
||||
import page = require("ui/page");
|
||||
import fileResolverModule = require("file-system/file-name-resolver");
|
||||
import trace = require("trace");
|
||||
import debug = require("utils/debug");
|
||||
import builder = require("ui/builder");
|
||||
import * as trace from "trace";
|
||||
import {debug, ScopeError, SourceError, Source} from "utils/debug";
|
||||
import * as xml from "xml";
|
||||
import {View, Template} from "ui/core/view";
|
||||
import {File, path, knownFolders} from "file-system";
|
||||
import {isString, isFunction, isDefined} from "utils/types";
|
||||
import {ComponentModule, setPropertyValue, getComponentModule} from "ui/builder/component-builder";
|
||||
import {platformNames, device} from "platform";
|
||||
import {LoadOptions} from "ui/builder";
|
||||
import {Page} from "ui/page";
|
||||
import {resolveFileName} from "file-system/file-name-resolver";
|
||||
|
||||
export function parse(value: string | view.Template, context: any): view.View {
|
||||
if (types.isString(value)) {
|
||||
var viewToReturn: view.View;
|
||||
|
||||
if (context instanceof view.View) {
|
||||
export function parse(value: string | Template, context: any): View {
|
||||
if (isString(value)) {
|
||||
var viewToReturn: View;
|
||||
|
||||
if (context instanceof View) {
|
||||
context = getExports(context);
|
||||
}
|
||||
|
||||
|
||||
var componentModule = parseInternal(<string>value, context);
|
||||
|
||||
|
||||
if (componentModule) {
|
||||
viewToReturn = componentModule.component;
|
||||
}
|
||||
|
||||
|
||||
return viewToReturn;
|
||||
} else if (types.isFunction(value)) {
|
||||
return (<view.Template>value)();
|
||||
} else if (isFunction(value)) {
|
||||
return (<Template>value)();
|
||||
}
|
||||
}
|
||||
|
||||
function parseInternal(value: string, context: any, uri?: string): componentBuilder.ComponentModule {
|
||||
|
||||
function parseInternal(value: string, context: any, uri?: string): ComponentModule {
|
||||
var start: xml2ui.XmlStringParser;
|
||||
var ui: xml2ui.ComponentParser;
|
||||
|
||||
var errorFormat = (debug.debug && uri) ? xml2ui.SourceErrorFormat(uri) : xml2ui.PositionErrorFormat;
|
||||
|
||||
|
||||
var errorFormat = (debug && uri) ? xml2ui.SourceErrorFormat(uri) : xml2ui.PositionErrorFormat;
|
||||
|
||||
(start = new xml2ui.XmlStringParser(errorFormat))
|
||||
.pipe(new xml2ui.PlatformFilter())
|
||||
.pipe(new xml2ui.XmlStateParser(ui = new xml2ui.ComponentParser(context, errorFormat)));
|
||||
@@ -47,44 +45,44 @@ function parseInternal(value: string, context: any, uri?: string): componentBuil
|
||||
return ui.rootComponentModule;
|
||||
}
|
||||
|
||||
function loadCustomComponent(componentPath: string, componentName?: string, attributes?: Object, context?: Object, parentPage?: page.Page): componentBuilder.ComponentModule {
|
||||
var result: componentBuilder.ComponentModule;
|
||||
function loadCustomComponent(componentPath: string, componentName?: string, attributes?: Object, context?: Object, parentPage?: Page): ComponentModule {
|
||||
var result: ComponentModule;
|
||||
componentPath = componentPath.replace("~/", "");
|
||||
|
||||
var fullComponentPathFilePathWithoutExt = componentPath;
|
||||
|
||||
if (!fs.File.exists(componentPath) || componentPath === "." || componentPath === "./") {
|
||||
fullComponentPathFilePathWithoutExt = fs.path.join(fs.knownFolders.currentApp().path, componentPath, componentName);
|
||||
if (!File.exists(componentPath) || componentPath === "." || componentPath === "./") {
|
||||
fullComponentPathFilePathWithoutExt = path.join(knownFolders.currentApp().path, componentPath, componentName);
|
||||
}
|
||||
|
||||
var xmlFilePath = fileResolverModule.resolveFileName(fullComponentPathFilePathWithoutExt, "xml");
|
||||
var xmlFilePath = resolveFileName(fullComponentPathFilePathWithoutExt, "xml");
|
||||
|
||||
if (xmlFilePath) {
|
||||
// Custom components with XML
|
||||
var jsFilePath = fileResolverModule.resolveFileName(fullComponentPathFilePathWithoutExt, "js");
|
||||
var jsFilePath = resolveFileName(fullComponentPathFilePathWithoutExt, "js");
|
||||
|
||||
var subExports = context;
|
||||
if (jsFilePath) {
|
||||
// Custom components with XML and code
|
||||
subExports = require(jsFilePath)
|
||||
subExports = global.loadModule(jsFilePath)
|
||||
}
|
||||
|
||||
result = loadInternal(xmlFilePath, subExports);
|
||||
|
||||
// Attributes will be transfered to the custom component
|
||||
if (types.isDefined(result) && types.isDefined(result.component) && types.isDefined(attributes)) {
|
||||
if (isDefined(result) && isDefined(result.component) && isDefined(attributes)) {
|
||||
var attr: string;
|
||||
for (attr in attributes) {
|
||||
componentBuilder.setPropertyValue(result.component, subExports, context, attr, attributes[attr]);
|
||||
setPropertyValue(result.component, subExports, context, attr, attributes[attr]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Custom components without XML
|
||||
result = componentBuilder.getComponentModule(componentName, componentPath, attributes, context);
|
||||
result = getComponentModule(componentName, componentPath, attributes, context);
|
||||
}
|
||||
|
||||
// Add component CSS file if exists.
|
||||
var cssFilePath = fileResolverModule.resolveFileName(fullComponentPathFilePathWithoutExt, "css");
|
||||
var cssFilePath = resolveFileName(fullComponentPathFilePathWithoutExt, "css");
|
||||
if (cssFilePath) {
|
||||
if (parentPage) {
|
||||
parentPage.addCssFile(cssFilePath);
|
||||
@@ -96,13 +94,13 @@ function loadCustomComponent(componentPath: string, componentName?: string, attr
|
||||
return result;
|
||||
}
|
||||
|
||||
export function load(pathOrOptions: string | definition.LoadOptions, context?: any): view.View {
|
||||
var viewToReturn: view.View;
|
||||
var componentModule: componentBuilder.ComponentModule;
|
||||
export function load(pathOrOptions: string | LoadOptions, context?: any): View {
|
||||
var viewToReturn: View;
|
||||
var componentModule: ComponentModule;
|
||||
|
||||
if (!context) {
|
||||
if (!types.isString(pathOrOptions)) {
|
||||
let options = <definition.LoadOptions>pathOrOptions;
|
||||
if (!isString(pathOrOptions)) {
|
||||
let options = <LoadOptions>pathOrOptions;
|
||||
componentModule = loadCustomComponent(options.path, options.name, undefined, options.exports, options.page);
|
||||
} else {
|
||||
let path = <string>pathOrOptions;
|
||||
@@ -120,12 +118,12 @@ export function load(pathOrOptions: string | definition.LoadOptions, context?: a
|
||||
return viewToReturn;
|
||||
}
|
||||
|
||||
function loadInternal(fileName: string, context?: any): componentBuilder.ComponentModule {
|
||||
var componentModule: componentBuilder.ComponentModule;
|
||||
function loadInternal(fileName: string, context?: any): ComponentModule {
|
||||
var componentModule: ComponentModule;
|
||||
|
||||
// Check if the XML file exists.
|
||||
if (fs.File.exists(fileName)) {
|
||||
var file = fs.File.fromPath(fileName);
|
||||
if (File.exists(fileName)) {
|
||||
var file = File.fromPath(fileName);
|
||||
var onError = function (error) {
|
||||
throw new Error("Error loading file " + fileName + " :" + error.message);
|
||||
}
|
||||
@@ -141,7 +139,7 @@ function loadInternal(fileName: string, context?: any): componentBuilder.Compone
|
||||
return componentModule;
|
||||
}
|
||||
|
||||
function getExports(instance: view.View): any {
|
||||
function getExports(instance: View): any {
|
||||
var parent = instance.parent;
|
||||
|
||||
while (parent && (<any>parent).exports === undefined) {
|
||||
@@ -152,7 +150,6 @@ function getExports(instance: view.View): any {
|
||||
}
|
||||
|
||||
namespace xml2ui {
|
||||
|
||||
/**
|
||||
* Pipes and filters:
|
||||
* https://en.wikipedia.org/wiki/Pipeline_(software)
|
||||
@@ -160,11 +157,11 @@ namespace xml2ui {
|
||||
interface XmlProducer {
|
||||
pipe<Next extends XmlConsumer>(next: Next): Next;
|
||||
}
|
||||
|
||||
|
||||
interface XmlConsumer {
|
||||
parse(args: xml.ParserEvent);
|
||||
}
|
||||
|
||||
|
||||
export class XmlProducerBase implements XmlProducer {
|
||||
private _next: XmlConsumer;
|
||||
public pipe<Next extends XmlConsumer>(next: Next) {
|
||||
@@ -175,15 +172,15 @@ namespace xml2ui {
|
||||
this._next.parse(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class XmlStringParser extends XmlProducerBase implements XmlProducer {
|
||||
private error: ErrorFormatter;
|
||||
|
||||
|
||||
constructor(error?: ErrorFormatter) {
|
||||
super();
|
||||
this.error = error || PositionErrorFormat;
|
||||
}
|
||||
|
||||
|
||||
public parse(value: string) {
|
||||
var xmlParser = new xml.XmlParser((args: xml.ParserEvent) => {
|
||||
try {
|
||||
@@ -194,80 +191,80 @@ namespace xml2ui {
|
||||
}, (e, p) => {
|
||||
throw this.error(e, p);
|
||||
}, true);
|
||||
|
||||
if (types.isString(value)) {
|
||||
|
||||
if (isString(value)) {
|
||||
value = value.replace(/xmlns=("|')http:\/\/((www)|(schemas))\.nativescript\.org\/tns\.xsd\1/, "");
|
||||
xmlParser.parse(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface ErrorFormatter {
|
||||
(e: Error, p: xml.Position): Error;
|
||||
}
|
||||
|
||||
|
||||
export function PositionErrorFormat(e: Error, p: xml.Position): Error {
|
||||
return new debug.ScopeError(e, "Parsing XML at " + p.line + ":" + p.column);
|
||||
return new ScopeError(e, "Parsing XML at " + p.line + ":" + p.column);
|
||||
}
|
||||
|
||||
|
||||
export function SourceErrorFormat(uri): ErrorFormatter {
|
||||
return (e: Error, p: xml.Position) => {
|
||||
var source = new debug.Source(uri, p.line, p.column);
|
||||
e = new debug.SourceError(e, source, "Building UI from XML.");
|
||||
var source = new Source(uri, p.line, p.column);
|
||||
e = new SourceError(e, source, "Building UI from XML.");
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class PlatformFilter extends XmlProducerBase implements XmlProducer, XmlConsumer {
|
||||
private currentPlatformContext: string;
|
||||
|
||||
|
||||
public parse(args: xml.ParserEvent) {
|
||||
if (args.eventType === xml.ParserEventType.StartElement) {
|
||||
if (PlatformFilter.isPlatform(args.elementName)) {
|
||||
|
||||
|
||||
if (this.currentPlatformContext) {
|
||||
throw new Error("Already in '" + this.currentPlatformContext + "' platform context and cannot switch to '" + args.elementName + "' platform! Platform tags cannot be nested.");
|
||||
}
|
||||
|
||||
|
||||
this.currentPlatformContext = args.elementName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (args.eventType === xml.ParserEventType.EndElement) {
|
||||
if (PlatformFilter.isPlatform(args.elementName)) {
|
||||
this.currentPlatformContext = undefined;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.currentPlatformContext && !PlatformFilter.isCurentPlatform(this.currentPlatformContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.next(args);
|
||||
}
|
||||
|
||||
|
||||
private static isPlatform(value: string): boolean {
|
||||
return value && (value.toLowerCase() === platform.platformNames.android.toLowerCase()
|
||||
|| value.toLowerCase() === platform.platformNames.ios.toLowerCase());
|
||||
return value && (value.toLowerCase() === platformNames.android.toLowerCase()
|
||||
|| value.toLowerCase() === platformNames.ios.toLowerCase());
|
||||
}
|
||||
|
||||
|
||||
private static isCurentPlatform(value: string): boolean {
|
||||
return value && value.toLowerCase() === platform.device.os.toLowerCase();
|
||||
return value && value.toLowerCase() === device.os.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class XmlArgsReplay extends XmlProducerBase implements XmlProducer {
|
||||
private error: ErrorFormatter;
|
||||
private args: xml.ParserEvent[];
|
||||
|
||||
|
||||
constructor(args: xml.ParserEvent[], errorFormat: ErrorFormatter) {
|
||||
super();
|
||||
this.args = args;
|
||||
this.error = errorFormat;
|
||||
}
|
||||
|
||||
|
||||
public replay() {
|
||||
this.args.forEach((args: xml.ParserEvent) => {
|
||||
try {
|
||||
@@ -278,68 +275,68 @@ namespace xml2ui {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface TemplateProperty {
|
||||
context?: any;
|
||||
parent: componentBuilder.ComponentModule;
|
||||
parent: ComponentModule;
|
||||
name: string;
|
||||
elementName: string;
|
||||
templateItems: Array<string>;
|
||||
errorFormat: ErrorFormatter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It is a state pattern
|
||||
* https://en.wikipedia.org/wiki/State_pattern
|
||||
*/
|
||||
export class XmlStateParser implements XmlConsumer {
|
||||
private state: XmlStateConsumer;
|
||||
|
||||
|
||||
constructor(state: XmlStateConsumer) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
|
||||
parse(args: xml.ParserEvent) {
|
||||
this.state = this.state.parse(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface XmlStateConsumer extends XmlConsumer {
|
||||
parse(args: xml.ParserEvent): XmlStateConsumer;
|
||||
}
|
||||
|
||||
|
||||
export class TemplateParser implements XmlStateConsumer {
|
||||
|
||||
|
||||
private _context: any;
|
||||
private _recordedXmlStream: Array<xml.ParserEvent>;
|
||||
private _templateProperty: TemplateProperty;
|
||||
private _nestingLevel: number;
|
||||
private _state: TemplateParser.State;
|
||||
|
||||
|
||||
private parent: XmlStateConsumer;
|
||||
|
||||
|
||||
constructor(parent: XmlStateConsumer, templateProperty: TemplateProperty) {
|
||||
this.parent = parent;
|
||||
|
||||
|
||||
this._context = templateProperty.context;
|
||||
this._recordedXmlStream = new Array<xml.ParserEvent>();
|
||||
this._templateProperty = templateProperty;
|
||||
this._nestingLevel = 0;
|
||||
this._state = TemplateParser.State.EXPECTING_START;
|
||||
}
|
||||
|
||||
|
||||
public parse(args: xml.ParserEvent): XmlStateConsumer {
|
||||
if (args.eventType === xml.ParserEventType.StartElement) {
|
||||
this.parseStartElement(args.prefix, args.namespace, args.elementName, args.attributes);
|
||||
} else if (args.eventType === xml.ParserEventType.EndElement) {
|
||||
this.parseEndElement(args.prefix, args.elementName);
|
||||
}
|
||||
|
||||
|
||||
this._recordedXmlStream.push(args);
|
||||
|
||||
|
||||
return this._state === TemplateParser.State.FINISHED ? this.parent : this;
|
||||
}
|
||||
|
||||
|
||||
public get elementName(): string {
|
||||
return this._templateProperty.elementName;
|
||||
}
|
||||
@@ -350,46 +347,46 @@ namespace xml2ui {
|
||||
} else if (this._state === TemplateParser.State.FINISHED) {
|
||||
throw new Error("Template must have exactly one root element but multiple elements were found.");
|
||||
}
|
||||
|
||||
|
||||
this._nestingLevel++;
|
||||
}
|
||||
|
||||
|
||||
private parseEndElement(prefix: string, elementName: string) {
|
||||
if (this._state === TemplateParser.State.EXPECTING_START) {
|
||||
throw new Error("Template must have exactly one root element but none was found.");
|
||||
} else if (this._state === TemplateParser.State.FINISHED) {
|
||||
throw new Error("No more closing elements expected for this template.");
|
||||
}
|
||||
|
||||
|
||||
this._nestingLevel--;
|
||||
|
||||
|
||||
if (this._nestingLevel === 0) {
|
||||
this._state = TemplateParser.State.FINISHED;
|
||||
this.build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private build() {
|
||||
if (this._templateProperty.name in this._templateProperty.parent.component) {
|
||||
var context = this._context;
|
||||
var errorFormat = this._templateProperty.errorFormat;
|
||||
var template: view.Template = () => {
|
||||
var template: Template = () => {
|
||||
var start: xml2ui.XmlArgsReplay;
|
||||
var ui: xml2ui.ComponentParser;
|
||||
|
||||
|
||||
(start = new xml2ui.XmlArgsReplay(this._recordedXmlStream, errorFormat))
|
||||
// No platform filter, it has been filtered allready
|
||||
.pipe(new XmlStateParser(ui = new ComponentParser(context, errorFormat)));
|
||||
|
||||
|
||||
start.replay();
|
||||
|
||||
|
||||
return ui.rootComponentModule.component;
|
||||
}
|
||||
this._templateProperty.parent.component[this._templateProperty.name] = template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TemplateParser {
|
||||
export const enum State {
|
||||
EXPECTING_START,
|
||||
@@ -397,45 +394,45 @@ namespace xml2ui {
|
||||
FINISHED
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ComponentParser implements XmlStateConsumer {
|
||||
|
||||
|
||||
private static KNOWNCOLLECTIONS = "knownCollections";
|
||||
private static KNOWNTEMPLATES = "knownTemplates";
|
||||
|
||||
public rootComponentModule: componentBuilder.ComponentModule;
|
||||
|
||||
|
||||
public rootComponentModule: ComponentModule;
|
||||
|
||||
private context: any;
|
||||
|
||||
private currentPage: page.Page;
|
||||
private parents = new Array<componentBuilder.ComponentModule>();
|
||||
|
||||
private currentPage: Page;
|
||||
private parents = new Array<ComponentModule>();
|
||||
private complexProperties = new Array<ComponentParser.ComplexProperty>();
|
||||
|
||||
|
||||
private error;
|
||||
|
||||
constructor(context: any, errorFormat: ErrorFormatter) {
|
||||
this.context = context;
|
||||
this.error = errorFormat;
|
||||
}
|
||||
|
||||
|
||||
public parse(args: xml.ParserEvent): XmlStateConsumer {
|
||||
|
||||
|
||||
// Get the current parent.
|
||||
var parent = this.parents[this.parents.length - 1];
|
||||
var complexProperty = this.complexProperties[this.complexProperties.length - 1];
|
||||
|
||||
|
||||
// Create component instance from every element declaration.
|
||||
if (args.eventType === xml.ParserEventType.StartElement) {
|
||||
if (ComponentParser.isComplexProperty(args.elementName)) {
|
||||
|
||||
|
||||
var name = ComponentParser.getComplexPropertyName(args.elementName);
|
||||
|
||||
|
||||
this.complexProperties.push({
|
||||
parent: parent,
|
||||
name: name,
|
||||
items: [],
|
||||
});
|
||||
|
||||
|
||||
if (ComponentParser.isKnownTemplate(name, parent.exports)) {
|
||||
return new TemplateParser(this, {
|
||||
context: (parent ? getExports(parent.component) : null) || this.context, // Passing 'context' won't work if you set "codeFile" on the page
|
||||
@@ -446,19 +443,19 @@ namespace xml2ui {
|
||||
errorFormat: this.error
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
var componentModule: componentBuilder.ComponentModule;
|
||||
|
||||
|
||||
var componentModule: ComponentModule;
|
||||
|
||||
if (args.prefix && args.namespace) {
|
||||
// Custom components
|
||||
componentModule = loadCustomComponent(args.namespace, args.elementName, args.attributes, this.context, this.currentPage);
|
||||
} else {
|
||||
// Default components
|
||||
componentModule = componentBuilder.getComponentModule(args.elementName, args.namespace, args.attributes, this.context);
|
||||
componentModule = getComponentModule(args.elementName, args.namespace, args.attributes, this.context);
|
||||
}
|
||||
|
||||
|
||||
if (componentModule) {
|
||||
if (parent) {
|
||||
if (complexProperty) {
|
||||
@@ -470,17 +467,17 @@ namespace xml2ui {
|
||||
} else if (this.parents.length === 0) {
|
||||
// Set root component.
|
||||
this.rootComponentModule = componentModule;
|
||||
|
||||
if (this.rootComponentModule && this.rootComponentModule.component instanceof page.Page) {
|
||||
this.currentPage = <page.Page>this.rootComponentModule.component;
|
||||
|
||||
if (this.rootComponentModule && this.rootComponentModule.component instanceof Page) {
|
||||
this.currentPage = <Page>this.rootComponentModule.component;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the component instance to the parents scope collection.
|
||||
this.parents.push(componentModule);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (args.eventType === xml.ParserEventType.EndElement) {
|
||||
if (ComponentParser.isComplexProperty(args.elementName)) {
|
||||
if (complexProperty) {
|
||||
@@ -492,36 +489,36 @@ namespace xml2ui {
|
||||
}
|
||||
// Remove the last complexProperty from the complexProperties collection (move to the previous complexProperty scope).
|
||||
this.complexProperties.pop();
|
||||
|
||||
|
||||
} else {
|
||||
// Remove the last parent from the parents collection (move to the previous parent scope).
|
||||
this.parents.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private static isComplexProperty(name: string): boolean {
|
||||
return types.isString(name) && name.indexOf(".") !== -1;
|
||||
return isString(name) && name.indexOf(".") !== -1;
|
||||
}
|
||||
|
||||
|
||||
private static getComplexPropertyName(fullName: string): string {
|
||||
var name: string;
|
||||
|
||||
if (types.isString(fullName)) {
|
||||
|
||||
if (isString(fullName)) {
|
||||
var names = fullName.split(".");
|
||||
name = names[names.length - 1];
|
||||
}
|
||||
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
private static isKnownTemplate(name: string, exports: any): boolean {
|
||||
return ComponentParser.KNOWNTEMPLATES in exports && exports[ComponentParser.KNOWNTEMPLATES] && name in exports[ComponentParser.KNOWNTEMPLATES];
|
||||
}
|
||||
|
||||
private static addToComplexProperty(parent: componentBuilder.ComponentModule, complexProperty: ComponentParser.ComplexProperty, elementModule: componentBuilder.ComponentModule) {
|
||||
private static addToComplexProperty(parent: ComponentModule, complexProperty: ComponentParser.ComplexProperty, elementModule: ComponentModule) {
|
||||
// If property name is known collection we populate array with elements.
|
||||
var parentComponent = <any>parent.component;
|
||||
if (ComponentParser.isKnownCollection(complexProperty.name, parent.exports)) {
|
||||
@@ -533,17 +530,17 @@ namespace xml2ui {
|
||||
parentComponent[complexProperty.name] = elementModule.component;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static isKnownCollection(name: string, context: any): boolean {
|
||||
return ComponentParser.KNOWNCOLLECTIONS in context && context[ComponentParser.KNOWNCOLLECTIONS] && name in context[ComponentParser.KNOWNCOLLECTIONS];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace ComponentParser {
|
||||
export interface ComplexProperty {
|
||||
parent: componentBuilder.ComponentModule;
|
||||
parent: ComponentModule;
|
||||
name: string;
|
||||
items?: Array<any>;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import view = require("ui/core/view");
|
||||
import types = require("utils/types");
|
||||
import definition = require("ui/builder/component-builder");
|
||||
import fs = require("file-system");
|
||||
import bindingBuilder = require("./binding-builder");
|
||||
import platform = require("platform");
|
||||
import pages = require("ui/page");
|
||||
import debug = require("utils/debug");
|
||||
import {isString, isDefined, isFunction} from "utils/types";
|
||||
import {device} from "platform";
|
||||
import {Page} from "ui/page";
|
||||
import {View, isEventOrGesture} from "ui/core/view";
|
||||
import {ComponentModule} from "ui/builder/component-builder";
|
||||
import {File, Folder, path, knownFolders} from "file-system";
|
||||
import {getBindingOptions, bindingConstants} from "./binding-builder";
|
||||
import {ScopeError} from "utils/debug";
|
||||
|
||||
//the imports below are needed for special property registration
|
||||
import "ui/layouts/dock-layout";
|
||||
@@ -27,10 +27,10 @@ var MODULES = {
|
||||
var CODEFILE = "codeFile";
|
||||
var CSSFILE = "cssFile";
|
||||
|
||||
export function getComponentModule(elementName: string, namespace: string, attributes: Object, exports: Object): definition.ComponentModule {
|
||||
var instance: view.View;
|
||||
export function getComponentModule(elementName: string, namespace: string, attributes: Object, exports: Object): ComponentModule {
|
||||
var instance: View;
|
||||
var instanceModule: Object;
|
||||
var componentModule: definition.ComponentModule;
|
||||
var componentModule: ComponentModule;
|
||||
|
||||
// Support lower-case-dashed component declaration in the XML (https://github.com/NativeScript/NativeScript/issues/309).
|
||||
elementName = elementName.split("-").map(s => { return s[0].toUpperCase() + s.substring(1) }).join("");
|
||||
@@ -41,19 +41,19 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
elementName.split(/(?=[A-Z])/).join("-").toLowerCase();
|
||||
|
||||
try {
|
||||
if (types.isString(namespace)) {
|
||||
var pathInsideTNSModules = fs.path.join(fs.knownFolders.currentApp().path, "tns_modules", namespace);
|
||||
if (isString(namespace)) {
|
||||
var pathInsideTNSModules = path.join(knownFolders.currentApp().path, "tns_modules", namespace);
|
||||
|
||||
if (fs.Folder.exists(pathInsideTNSModules)) {
|
||||
if (Folder.exists(pathInsideTNSModules)) {
|
||||
moduleId = pathInsideTNSModules;
|
||||
} else {
|
||||
// We expect module at root level in the app.
|
||||
moduleId = fs.path.join(fs.knownFolders.currentApp().path, namespace);
|
||||
moduleId = path.join(knownFolders.currentApp().path, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
// Require module by module id.
|
||||
instanceModule = require(moduleId);
|
||||
instanceModule = global.loadModule(moduleId);
|
||||
|
||||
// Get the component type from module.
|
||||
var instanceType = instanceModule[elementName] || Object;
|
||||
@@ -61,18 +61,18 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
// Create instance of the component.
|
||||
instance = new instanceType();
|
||||
} catch (ex) {
|
||||
throw new debug.ScopeError(ex, "Module '" + moduleId + "' not found for element '" + (namespace ? namespace + ":" : "") + elementName + "'.");
|
||||
throw new ScopeError(ex, "Module '" + moduleId + "' not found for element '" + (namespace ? namespace + ":" : "") + elementName + "'.");
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
if (attributes[CODEFILE]) {
|
||||
if (instance instanceof pages.Page) {
|
||||
if (instance instanceof Page) {
|
||||
var codeFilePath = attributes[CODEFILE].trim();
|
||||
if (codeFilePath.indexOf("~/") === 0) {
|
||||
codeFilePath = fs.path.join(fs.knownFolders.currentApp().path, codeFilePath.replace("~/", ""));
|
||||
codeFilePath = path.join(knownFolders.currentApp().path, codeFilePath.replace("~/", ""));
|
||||
}
|
||||
try {
|
||||
exports = require(codeFilePath);
|
||||
exports = global.loadModule(codeFilePath);
|
||||
(<any>instance).exports = exports;
|
||||
} catch (ex) {
|
||||
throw new Error(`Code file with path "${codeFilePath}" cannot be found!`);
|
||||
@@ -83,13 +83,13 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
}
|
||||
|
||||
if (attributes[CSSFILE]) {
|
||||
if (instance instanceof pages.Page) {
|
||||
if (instance instanceof Page) {
|
||||
var cssFilePath = attributes[CSSFILE].trim();
|
||||
if (cssFilePath.indexOf("~/") === 0) {
|
||||
cssFilePath = fs.path.join(fs.knownFolders.currentApp().path, cssFilePath.replace("~/", ""));
|
||||
cssFilePath = path.join(knownFolders.currentApp().path, cssFilePath.replace("~/", ""));
|
||||
}
|
||||
if (fs.File.exists(cssFilePath)) {
|
||||
(<pages.Page>instance).addCssFile(cssFilePath);
|
||||
if (File.exists(cssFilePath)) {
|
||||
(<Page>instance).addCssFile(cssFilePath);
|
||||
instance[CSSFILE] = true;
|
||||
} else {
|
||||
throw new Error(`Css file with path "${cssFilePath}" cannot be found!`);
|
||||
@@ -107,7 +107,7 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
|
||||
if (attr.indexOf(":") !== -1) {
|
||||
var platformName = attr.split(":")[0].trim();
|
||||
if (platformName.toLowerCase() === platform.device.os.toLowerCase()) {
|
||||
if (platformName.toLowerCase() === device.os.toLowerCase()) {
|
||||
attr = attr.split(":")[1].trim();
|
||||
} else {
|
||||
continue;
|
||||
@@ -121,12 +121,12 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
|
||||
var i: number;
|
||||
for (i = 0; i < properties.length - 1; i++) {
|
||||
if (types.isDefined(subObj)) {
|
||||
if (isDefined(subObj)) {
|
||||
subObj = subObj[properties[i]];
|
||||
}
|
||||
}
|
||||
|
||||
if (types.isDefined(subObj)) {
|
||||
if (isDefined(subObj)) {
|
||||
setPropertyValue(subObj, instanceModule, exports, subPropName, attrValue);
|
||||
}
|
||||
} else {
|
||||
@@ -140,23 +140,23 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
||||
return componentModule;
|
||||
}
|
||||
|
||||
export function setPropertyValue(instance: view.View, instanceModule: Object, exports: Object, propertyName: string, propertyValue: string) {
|
||||
export function setPropertyValue(instance: View, instanceModule: Object, exports: Object, propertyName: string, propertyValue: string) {
|
||||
// Note: instanceModule can be null if we are loading custom compnenet with no code-behind.
|
||||
|
||||
if (isBinding(propertyValue) && instance.bind) {
|
||||
var bindOptions = bindingBuilder.getBindingOptions(propertyName, getBindingExpressionFromAttribute(propertyValue));
|
||||
var bindOptions = getBindingOptions(propertyName, getBindingExpressionFromAttribute(propertyValue));
|
||||
instance.bind({
|
||||
sourceProperty: bindOptions[bindingBuilder.bindingConstants.sourceProperty],
|
||||
targetProperty: bindOptions[bindingBuilder.bindingConstants.targetProperty],
|
||||
expression: bindOptions[bindingBuilder.bindingConstants.expression],
|
||||
twoWay: bindOptions[bindingBuilder.bindingConstants.twoWay]
|
||||
}, bindOptions[bindingBuilder.bindingConstants.source]);
|
||||
} else if (view.isEventOrGesture(propertyName, instance)) {
|
||||
sourceProperty: bindOptions[bindingConstants.sourceProperty],
|
||||
targetProperty: bindOptions[bindingConstants.targetProperty],
|
||||
expression: bindOptions[bindingConstants.expression],
|
||||
twoWay: bindOptions[bindingConstants.twoWay]
|
||||
}, bindOptions[bindingConstants.source]);
|
||||
} else if (isEventOrGesture(propertyName, instance)) {
|
||||
// Get the event handler from page module exports.
|
||||
var handler = exports && exports[propertyValue];
|
||||
|
||||
// Check if the handler is function and add it to the instance for specified event name.
|
||||
if (types.isFunction(handler)) {
|
||||
if (isFunction(handler)) {
|
||||
instance.on(propertyName, handler);
|
||||
}
|
||||
} else {
|
||||
@@ -194,7 +194,7 @@ function getBindingExpressionFromAttribute(value: string): string {
|
||||
function isBinding(value: string): boolean {
|
||||
var isBinding;
|
||||
|
||||
if (types.isString(value)) {
|
||||
if (isString(value)) {
|
||||
var str = value.trim();
|
||||
isBinding = str.indexOf("{{") === 0 && str.lastIndexOf("}}") === str.length - 2;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import definition = require("ui/frame");
|
||||
import view = require("ui/core/view");
|
||||
import pages = require("ui/page");
|
||||
import types = require("utils/types");
|
||||
import trace = require("trace");
|
||||
import builder = require("ui/builder");
|
||||
import fs = require("file-system");
|
||||
import fileResolverModule = require("file-system/file-name-resolver");
|
||||
import * as definition from "ui/frame";
|
||||
import {View, CustomLayoutView} from "ui/core/view";
|
||||
import {Page} from "ui/page";
|
||||
import {isString, isFunction, isDefined} from "utils/types";
|
||||
import * as trace from "trace";
|
||||
import {load as buildModule} from "ui/builder";
|
||||
import {knownFolders, path} from "file-system";
|
||||
import {resolveFileName} from "file-system/file-name-resolver";
|
||||
|
||||
var frameStack: Array<Frame> = [];
|
||||
|
||||
function buildEntryFromArgs(arg: any): definition.NavigationEntry {
|
||||
var entry: definition.NavigationEntry;
|
||||
if (arg instanceof pages.Page) {
|
||||
if (arg instanceof Page) {
|
||||
throw new Error("Navigating to a Page instance is no longer supported. Please navigate by using either a module name or a page factory function.");
|
||||
} else if (types.isString(arg)) {
|
||||
} else if (isString(arg)) {
|
||||
entry = {
|
||||
moduleName: arg
|
||||
};
|
||||
} else if (types.isFunction(arg)) {
|
||||
} else if (isFunction(arg)) {
|
||||
entry = {
|
||||
create: arg
|
||||
}
|
||||
@@ -45,30 +45,35 @@ export function reloadPage(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function resolvePageFromEntry(entry: definition.NavigationEntry): pages.Page {
|
||||
var page: pages.Page;
|
||||
export function resolvePageFromEntry(entry: definition.NavigationEntry): Page {
|
||||
var page: Page;
|
||||
|
||||
if (entry.create) {
|
||||
page = entry.create();
|
||||
|
||||
if (!(page && page instanceof pages.Page)) {
|
||||
if (!(page && page instanceof Page)) {
|
||||
throw new Error("Failed to create Page with entry.create() function.");
|
||||
}
|
||||
}
|
||||
else if (entry.moduleName) {
|
||||
// Current app full path.
|
||||
var currentAppPath = fs.knownFolders.currentApp().path;
|
||||
var currentAppPath = knownFolders.currentApp().path;
|
||||
//Full path of the module = current app full path + module name.
|
||||
var moduleNamePath = fs.path.join(currentAppPath, entry.moduleName);
|
||||
var moduleNamePath = path.join(currentAppPath, entry.moduleName);
|
||||
|
||||
var moduleExports;
|
||||
var moduleExportsResolvedPath = fileResolverModule.resolveFileName(moduleNamePath, "js");
|
||||
if (moduleExportsResolvedPath) {
|
||||
trace.write("Loading JS file: " + moduleExportsResolvedPath, trace.categories.Navigation);
|
||||
|
||||
// Exclude extension when doing require.
|
||||
moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3)
|
||||
moduleExports = require(moduleExportsResolvedPath);
|
||||
if (global.moduleExists(entry.moduleName)) {
|
||||
trace.write("Loading pre-registered JS module: " + entry.moduleName, trace.categories.Navigation);
|
||||
moduleExports = global.loadModule(entry.moduleName);
|
||||
} else {
|
||||
var moduleExportsResolvedPath = resolveFileName(moduleNamePath, "js");
|
||||
if (moduleExportsResolvedPath) {
|
||||
trace.write("Loading JS file: " + moduleExportsResolvedPath, trace.categories.Navigation);
|
||||
|
||||
// Exclude extension when doing require.
|
||||
moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3)
|
||||
moduleExports = global.loadModule(moduleExportsResolvedPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleExports && moduleExports.createPage) {
|
||||
@@ -79,12 +84,12 @@ export function resolvePageFromEntry(entry: definition.NavigationEntry): pages.P
|
||||
page = pageFromBuilder(moduleNamePath, moduleExports);
|
||||
}
|
||||
|
||||
if (!(page && page instanceof pages.Page)) {
|
||||
if (!(page && page instanceof Page)) {
|
||||
throw new Error("Failed to load Page from entry.moduleName: " + entry.moduleName);
|
||||
}
|
||||
|
||||
// Possible CSS file path. Add it only if CSS not already specified and loaded from cssFile Page attribute in XML.
|
||||
var cssFileName = fileResolverModule.resolveFileName(moduleNamePath, "css");
|
||||
var cssFileName = resolveFileName(moduleNamePath, "css");
|
||||
if (cssFileName && !page["cssFile"]) {
|
||||
page.addCssFile(cssFileName);
|
||||
}
|
||||
@@ -93,19 +98,19 @@ export function resolvePageFromEntry(entry: definition.NavigationEntry): pages.P
|
||||
return page;
|
||||
}
|
||||
|
||||
function pageFromBuilder(moduleNamePath: string, moduleExports: any): pages.Page {
|
||||
var page: pages.Page;
|
||||
var element: view.View;
|
||||
function pageFromBuilder(moduleNamePath: string, moduleExports: any): Page {
|
||||
var page: Page;
|
||||
var element: View;
|
||||
|
||||
// Possible XML file path.
|
||||
var fileName = fileResolverModule.resolveFileName(moduleNamePath, "xml");
|
||||
var fileName = resolveFileName(moduleNamePath, "xml");
|
||||
if (fileName) {
|
||||
trace.write("Loading XML file: " + fileName, trace.categories.Navigation);
|
||||
|
||||
// Or check if the file exists in the app modules and load the page from XML.
|
||||
element = builder.load(fileName, moduleExports);
|
||||
if (element instanceof pages.Page) {
|
||||
page = <pages.Page>element;
|
||||
element = buildModule(fileName, moduleExports);
|
||||
if (element instanceof Page) {
|
||||
page = <Page>element;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +122,7 @@ interface NavigationContext {
|
||||
isBackNavigation: boolean;
|
||||
}
|
||||
|
||||
export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
export class Frame extends CustomLayoutView implements definition.Frame {
|
||||
public static androidOptionSelectedEvent = "optionSelected";
|
||||
|
||||
private _navigationQueue: Array<NavigationContext>;
|
||||
@@ -192,7 +197,7 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
}
|
||||
}
|
||||
|
||||
public _processNavigationQueue(page: pages.Page) {
|
||||
public _processNavigationQueue(page: Page) {
|
||||
if (this._navigationQueue.length === 0) {
|
||||
// This could happen when showing recreated page after activity has been destroyed.
|
||||
return;
|
||||
@@ -225,12 +230,12 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
}
|
||||
|
||||
var backstackVisibleValue = entry.entry.backstackVisible;
|
||||
var backstackHidden = types.isDefined(backstackVisibleValue) && !backstackVisibleValue;
|
||||
var backstackHidden = isDefined(backstackVisibleValue) && !backstackVisibleValue;
|
||||
|
||||
return !backstackHidden;
|
||||
}
|
||||
|
||||
public _updateActionBar(page?: pages.Page) {
|
||||
public _updateActionBar(page?: Page) {
|
||||
trace.write("calling _updateActionBar on Frame", trace.categories.Navigation);
|
||||
}
|
||||
|
||||
@@ -299,7 +304,7 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
return this._backStack.slice();
|
||||
}
|
||||
|
||||
get currentPage(): pages.Page {
|
||||
get currentPage(): Page {
|
||||
if (this._currentEntry) {
|
||||
return this._currentEntry.resolvedPage;
|
||||
}
|
||||
@@ -342,18 +347,18 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public _eachChildView(callback: (child: view.View) => boolean) {
|
||||
public _eachChildView(callback: (child: View) => boolean) {
|
||||
if (this.currentPage) {
|
||||
callback(this.currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
public _getIsAnimatedNavigation(entry: definition.NavigationEntry) {
|
||||
if (entry && types.isDefined(entry.animated)) {
|
||||
if (entry && isDefined(entry.animated)) {
|
||||
return entry.animated;
|
||||
}
|
||||
|
||||
if (types.isDefined(this.animated)) {
|
||||
if (isDefined(this.animated)) {
|
||||
return this.animated;
|
||||
}
|
||||
|
||||
@@ -368,17 +373,17 @@ export class Frame extends view.CustomLayoutView implements definition.Frame {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public _getNavBarVisible(page: pages.Page): boolean {
|
||||
public _getNavBarVisible(page: Page): boolean {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
// We don't need to put Page as visual child. Don't call super.
|
||||
public _addViewToNativeVisualTree(child: view.View): boolean {
|
||||
public _addViewToNativeVisualTree(child: View): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't need to put Page as visual child. Don't call super.
|
||||
public _removeViewFromNativeVisualTree(child: view.View): void {
|
||||
public _removeViewFromNativeVisualTree(child: View): void {
|
||||
child._isAddedToNativeVisualTree = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user