UNPKG

total4

Version:
2,155 lines (1,704 loc) 61.1 kB
'use strict'; const Crypto = require('crypto'); const Fs = require('fs'); const ReadStream = Fs.ReadStream; const Stream = require('stream'); const EMPTYARRAY = []; const EMPTYOBJECT = {}; if (!global.framework_utils) global.framework_utils = require('./utils'); Object.freeze(EMPTYOBJECT); Object.freeze(EMPTYARRAY); global.$STRING = function(value, encode) { value = value != null ? (value + '') : ''; return encode && value ? value.encode() : value; }; const REG_1 = /[\n\r\t]+/g; const REG_2 = /\s{2,}/g; const REG_4 = /\n\s{2,}./g; const REG_5 = />\n\s{1,}</g; const REG_6 = /[<\w"\u0080-\u07ff\u0400-\u04FF]+\s{2,}[\w\u0080-\u07ff\u0400-\u04FF>]+/; const REG_7 = /\\/g; const REG_8 = /'/g; const REG_9 = />\n\s+/g; const REG_10 = /(\w|\W)\n\s+</g; const REG_WIN = /\r/g; const REG_BLOCK_BEG = /@\{block.*?\}/i; const REG_BLOCK_END = /@\{end\}/i; const REG_SKIP_1 = /\('|"/; const REG_SKIP_2 = /,(\s)?\w+/; const REG_COMPONENTS_GROUP = /('|")[a-z0-9_]+('|")/i; const RENDERNOW = ['self.$import(', 'self.route', 'self.$js(', 'self.$css(', 'self.$favicon(', '$STRING(self.resource(', '$STRING(RESOURCE(', 'language', 'self.sitemap_url(', 'self.sitemap_name(', '$STRING(CONFIG(', '$STRING(config.', '$STRING(config[', '$STRING(CONF.', '$STRING(CONF[', '$STRING(config(']; const REG_NOTRANSLATE = /@\{notranslate|nolocalize\}/gi; const REG_NOCOMPRESS = /@\{nocompress\s\w+}/gi; const REG_TAGREMOVE = /[^>](\r)\n\s{1,}$/; const REG_HELPERS = /helpers\.[a-z0-9A-Z_$]+\(.*?\)+/g; const REG_SITEMAP = /\s+(sitemap_navigation\(|sitemap\()+/g; const REG_CSS_0 = /\s{2,}|\t/g; const REG_CSS_1 = /\n/g; const REG_CSS_2 = /\s?\{\s{1,}/g; const REG_CSS_3 = /\s?\}\s{1,}/g; const REG_CSS_4 = /\s?:\s{1,}/g; const REG_CSS_5 = /\s?;\s{1,}/g; const REG_CSS_6 = /,\s{1,}/g; const REG_CSS_7 = /\s\}/g; const REG_CSS_8 = /\s\{/g; const REG_CSS_9 = /;\}/g; const REG_CSS_10 = /\$[a-z0-9-_]+(\s)*:.*?;/gi; const REG_CSS_11 = /\$.*?(\s|;|\}|!)/gi; const REG_CSS_12 = /(margin|padding):.*?(;|})/g; const REG_CSS_13 = /#(0{6}|1{6}|2{6}|3{6}|4{6}|5{6}|6{6}|7{6}|8{6}|9{6}|0{6}|A{6}|B{6}|C{6}|D{6}|E{6}|F{6})+[\s;,)}]/gi; const REG_CSS_14 = /\s>\s/g; const REG_VIEW_PART = /\/\*PART.*?\*\//g; const AUTOVENDOR = ['appearance', 'user-select', 'font-smoothing', 'text-size-adjust', 'backface-visibility']; const ALLOWEDMARKUP = { G: 1, M: 1, R: 1, repository: 1, model: 1, CONF: 1, config: 1, global: 1, resource: 1, RESOURCE: 1, CONFIG: 1, author: 1, root: 1, functions: 1, NOW: 1, F: 1 }; global.$VIEWCACHE = []; global.$VIEWASYNC = 0; exports.routesplit = function(url, nolowercase) { var arr; if (!nolowercase) { url = url.toLowerCase(); arr = F.temporary.other[url]; if (arr) return arr; } if (!url || url === '/') { arr = ['/']; return arr; } var prev = false; var key = ''; var count = 0; arr = []; for (var i = 0; i < url.length; i++) { var c = url[i]; if (c === '/') { if (key && !prev) { arr.push(key); count++; key = ''; } continue; } key += c; prev = c === '/'; } if (key) arr.push(key); else if (!count) arr.push('/'); return arr; }; exports.routesplitcreate = function(url, nolowercase) { if (!nolowercase) url = url.toLowerCase(); if (url[0] === '/') url = url.substring(1); if (url[url.length - 1] === '/') url = url.substring(0, url.length - 1); var count = 0; var end = 0; var arr = []; for (var i = 0, length = url.length; i < length; i++) { switch (url[i]) { case '/': if (count !== 0) break; arr.push(url.substring(end + (arr.length ? 1 : 0), i)); end = i; break; case '{': count++; break; case '}': count--; break; } } !count && arr.push(url.substring(end + (arr.length ? 1 : 0), url.length)); if (arr.length === 1 && !arr[0]) arr[0] = '/'; return arr; }; exports.routecompare = function(url, route, isSystem, isWildcard) { var length = url.length; var lengthroute = route.length; if ((lengthroute !== length && !isWildcard) || (isWildcard && length < lengthroute)) return false; if (isWildcard && lengthroute === 1 && route[0] === '/') return true; var skip = length === 1 && url[0] === '/'; for (var i = 0; i < length; i++) { var value = route[i]; if (!isSystem && isWildcard && value == null) return true; if (!isSystem && (!skip && value[0] === '{')) continue; if (url[i] !== value) return isSystem ? false : isWildcard ? i >= lengthroute : false; } return true; }; exports.routeparams = function(url, route) { if (!route || !url || !route.param.length) return EMPTYARRAY; var arr = []; var is = false; for (var i = 0; i < route.param.length; i++) { var value = url[route.param[i]]; var name = route.paramnames[i]; switch (route.paramtypes[name]) { case 'uid': if (!value.isUID()) is = true; break; case 'guid': if (!value.isGUID()) is = true; break; case 'number': value = +value; if (isNaN(value)) { is = true; value = 0; } break; case 'boolean': value = value === 'true' || value === '1'; break; case 'date': value = value.length > 6 ? value.indexOf('-') === -1 ? value.parseDate('yyyyMMddHHmm') : value.parseDate('yyyy-MM-dd-HHmm') : null; if (value == null || !value.getTime) is = true; break; } arr.push(value === '/' ? '' : value); } return { values: arr, error: is }; }; function HttpFile() { // this.name; // this.filename; // this.type; // this.path; this.length = 0; this.width = 0; this.height = 0; this.rem = true; } HttpFile.prototype = { get size() { return this.length; }, get extension() { if (!this.$extension) this.$extension = framework_utils.getExtension(this.filename); return this.$extension; }, set extension(val) { this.$extension = val; } }; var HFP = HttpFile.prototype; HFP.rename = HFP.move = function(filename, callback) { var self = this; if (callback) return self._move(filename, callback); else return new Promise((resolve, reject) => self._move(filename, (err) => err ? reject(err) : resolve())); }; HFP._move = function(filename, callback) { var self = this; Fs.rename(self.path, filename, function(err) { if (err && err.code === 'EXDEV') { self.copy(filename, function(err){ Fs.unlink(self.path, NOOP); if (!err) { self.path = filename; self.rem = false; } callback && callback(err); }); return; } if (!err) { self.path = filename; self.rem = false; } callback && callback(err); }); return self; }; HFP.copy = function(filename, callback) { var self = this; if (callback) return self._copy(filename, callback); else return new Promise((resolve, reject) => self._copy(filename, (err) => err ? reject(err) : resolve())); }; HFP._copy = function(filename, callback) { var self = this; if (!callback) { Fs.createReadStream(self.path).pipe(Fs.createWriteStream(filename)); return; } var reader = Fs.createReadStream(self.path); var writer = Fs.createWriteStream(filename); reader.on('close', callback); reader.pipe(writer); return self; }; HFP.read = function(callback) { var self = this; if (callback) return self._read(callback); else return new Promise((resolve, reject) => self._read((err, res) => err ? reject(err) : resolve(res))); }; HFP._read = function(callback) { var self = this; Fs.readFile(self.path, callback); return self; }; HFP.md5 = function(callback) { var self = this; if (callback) return self._md5(callback); else return new Promise((resolve, reject) => self._md5((err, res) => err ? reject(err) : resolve(res))); }; HFP._md5 = function(callback) { var self = this; var md5 = Crypto.createHash('md5'); var stream = Fs.createReadStream(self.path); stream.on('data', (buffer) => md5.update(buffer)); stream.on('error', function(error) { if (callback) { callback(error, null); callback = null; } }); onFinished(stream, function() { destroyStream(stream); if (callback) { callback(null, md5.digest('hex')); callback = null; } }); return self; }; HFP.stream = function(options) { return Fs.createReadStream(this.path, options); }; HFP.pipe = function(stream, options) { return Fs.createReadStream(this.path, options).pipe(stream, options); }; HFP.isImage = function() { return this.type.indexOf('image/') !== -1; }; HFP.isVideo = function() { return this.type.indexOf('video/') !== -1; }; HFP.isAudio = function() { return this.type.indexOf('audio/') !== -1; }; HFP.image = function(im) { if (im === undefined) im = CONF.default_image_converter === 'im'; return framework_image.init(this.path, im, this.width, this.height); }; HFP.fs = function(storage, id, custom, expire, callback) { if (typeof(custom) === 'function') { callback = custom; custom = null; expire = null; } else if (typeof(expire) === 'function') { callback = expire; expire = null; } return FILESTORAGE(storage).save(id, this.filename, this.path, callback, custom, expire); }; // ********************************************************************************* // ================================================================================= // JS CSS + AUTO-VENDOR-PREFIXES // ================================================================================= // ********************************************************************************* function compile_autovendor(css) { var avp = '/*auto*/'; var isAuto = css.substring(0, 100).indexOf(avp) !== -1; if (isAuto) css = autoprefixer(css.replace(avp, '')); return css.replace(REG_CSS_0, ' ').replace(REG_CSS_1, '').replace(REG_CSS_2, '{').replace(REG_CSS_3, '}').replace(REG_CSS_4, ':').replace(REG_CSS_5, ';').replace(REG_CSS_6, function(search, index, text) { for (var i = index; i > 0; i--) { if ((text[i] === '\'' || text[i] === '"') && (text[i - 1] === ':')) return search; } return ','; }).replace(REG_CSS_7, '}').replace(REG_CSS_8, '{').replace(REG_CSS_9, '}').replace(REG_CSS_12, cssmarginpadding).replace(REG_CSS_13, csscolors).replace(REG_CSS_14, '>').trim(); } function csscolors(text) { return text.substring(0, 4) + text.charAt(text.length - 1); } function cssmarginpadding(text) { // margin // padding var prop = ''; var val; var l = text.length - 1; var last = text[l]; if (text[0] === 'm') { prop = 'margin:'; val = text.substring(7, l); } else { prop = 'padding:'; val = text.substring(8, l); } var a = val.split(' '); for (var i = 0; i < a.length; i++) { if (a[i][0] === '0' && a[i].charCodeAt(1) > 58) a[i] = '0'; } // 0 0 0 0 --> 0 if (a[0] === '0' && a[1] === '0' && a[2] === '0' && a[3] === '0') return prop + '0' + last; // 20px 0 0 0 --> 20px 0 0 if (a[0] !== '0' && a[1] === '0' && a[2] === '0' && a[3] === '0') return prop + a[0] + ' 0 0' + last; // 20px 30px 20px 30px --> 20px 30px if (a[1] && a[2] && a[3] && a[0] === a[2] && a[1] === a[3]) return prop + a[0] + ' ' + a[1] + last; // 20px 30px 10px 30px --> 20px 30px 10px if (a[2] && a[3] && a[1] === a[3] && a[0] !== a[2]) return prop + a[0] + ' ' + a[1] + ' ' + a[2] + last; return text; } function autoprefixer(value) { var builder = []; var index = 0; var property; // properties for (var i = 0, length = AUTOVENDOR.length; i < length; i++) { property = AUTOVENDOR[i]; index = 0; while (index !== -1) { index = value.indexOf(property, index + 1); if (index === -1) continue; var a = value.indexOf(';', index); var b = value.indexOf('}', index); var end = Math.min(a, b); if (end === -1) end = Math.max(a, b); if (end === -1) continue; var before = value.substring(index - 1, index); var isPrefix = before === '-' || before === '.' || before === '#'; if (isPrefix) continue; var css = value.substring(index, end); end = css.indexOf(':'); if (end === -1 || css.substring(0, end + 1).replace(/\s/g, '') !== property + ':') continue; builder.push({ name: property, property: before + css, css: css }); } } var output = []; var length = builder.length; for (var i = 0; i < length; i++) { var name = builder[i].name; var replace = builder[i].property; var before = replace[0]; property = builder[i].css.trim(); var plus = property; var delimiter = ';'; var updated = plus + delimiter; if (name === 'font-smoothing') { updated = plus + delimiter; updated += plus.replacer('font-smoothing', '-webkit-font-smoothing') + delimiter; updated += plus.replacer('font-smoothing', '-moz-osx-font-smoothing'); value = value.replacer(replace, before + '@[[' + output.length + ']]'); output.push(updated); continue; } updated += '-webkit-' + plus + delimiter; updated += '-moz-' + plus; value = value.replacer(replace, before + '@[[' + output.length + ']]'); output.push(updated); } length = output.length; for (var i = 0; i < length; i++) value = value.replacer('@[[' + i + ']]', output[i]); output = null; builder = null; return value; } function minify_javascript(data) { var index = 0; var output = []; var isCS = false; // comment multiline var isCI = false; // comment inline var alpha = /[0-9a-z$]/i; var white = /\W/; var skip = { '$': true, '_': true }; var newlines = { '\n': 1, '\r': 1 }; var regexp = false; var scope, prev, next, last; var vtmp = false; var regvar = /^(\s)*var /; var vindex = 0; while (true) { var c = data[index]; var prev = data[index - 1]; var next = data[index + 1]; index++; if (c === undefined) break; if (!scope) { if (!regexp) { if (!isCI && c === '/' && next === '*') { isCS = true; continue; } else if (!isCI && c === '*' && next === '/') { isCS = false; index++; continue; } if (isCS) continue; if (c === '/' && next === '/') { isCI = true; continue; } else if (isCI && newlines[c]) { isCI = false; alpha.test(last) && output.push(' '); last = ''; continue; } if (isCI) continue; } if (c === '\t' || newlines[c]) { if (!last || !alpha.test(last)) continue; output.push(' '); last = ''; continue; } if (!regexp && (c === ' ' && (white.test(prev) || white.test(next)))) { // if (!skip[prev] && !skip[next]) if (!skip[prev]) { if (!skip[next] || !alpha.test(prev)) continue; } } if (regexp) { if ((last !== '\\' && c === '/') || (last === '\\' && c === '/' && output[output.length - 2] === '\\')) regexp = false; } else regexp = (last === '=' || last === '(' || last === ':' || last === '{' || last === '[' || last === '?') && (c === '/'); } if (scope && c === '\\') { output.push(c); output.push(next); index++; last = next; continue; } if (!regexp && (c === '"' || c === '\'' || c === '`')) { if (scope && scope !== c) { output.push(c); continue; } if (c === scope) scope = 0; else scope = c; } // var if (!scope && c === 'v' && next === 'a') { var v = c + data[index] + data[index + 1] + data[index + 2]; if (v === 'var ') { if (vtmp && output[output.length - 1] === ';') { output.pop(); output.push(','); } else output.push('var '); index += 3; vtmp = true; continue; } } else { if (vtmp) { vindex = index + 1; while (true) { if (!data[vindex] || !white.test(data[vindex])) break; vindex++; } if (c === '(' || c === ')' || (c === ';' && !regvar.test(data.substring(vindex, vindex + 20)))) vtmp = false; } } if ((c === '+' || c === '-') && next === ' ') { if (data[index + 1] === c) { index += 2; output.push(c); output.push(' '); output.push(c); last = c; continue; } } if (!scope && (c === '}' && last === ';') || ((c === '}' || c === ']') && output[output.length - 1] === ' ' && alpha.test(output[output.length - 2]))) output.pop(); output.push(c); last = c; } return output.join('').trim(); } exports.compile_css = function(value, filename, nomarkup) { // Internal markup if (!nomarkup) value = markup(value, filename); if (global.F) { value = modificators(value, filename, 'style'); if (DEF.onCompileStyle) return DEF.onCompileStyle(filename, value); } try { var isVariable = false; value = nested(value, '', () => isVariable = true); value = compile_autovendor(value); if (isVariable) value = variablesCSS(value); return value; } catch (ex) { F.error(new Error('CSS compiler error: ' + ex.message)); return ''; } }; exports.compile_javascript = function(source, filename, nomarkup) { // Internal markup if (!nomarkup) source = markup(source, filename); if (global.F) { source = modificators(source, filename, 'script'); if (DEF.onCompileScript) return DEF.onCompileScript(filename, source).trim(); } return minify_javascript(source); }; exports.compile_html = function(source, filename, nomarkup) { return compressCSS(compressJS(compressHTML(source, true), 0, filename, nomarkup), 0, filename, nomarkup); }; // ********************************************************************************* // ================================================================================= // VIEW ENGINE // ================================================================================= // ********************************************************************************* function view_parse_localization(content, language) { var is = false; content = content.replace(REG_NOTRANSLATE, function() { is = true; return ''; }).trim(); if (is) return content; var command = view_find_localization(content, 0); var output = ''; var end = 0; if (!command) return content; while (command) { if (command) output += content.substring(end ? end + 1 : 0, command.beg) + (command.command ? localize(language, command) : ''); end = command.end; command = view_find_localization(content, command.end); } output += content.substring(end + 1); return output; } // Escaping ", ' and ` chars function localize(language, command) { !language && (language = 'default'); if (F.resources[language] && F.resources[language].$empty) return command.command; var output = TRANSLATE(language, command.command); if (command.escape) { var index = 0; while (true) { index = output.indexOf(command.escape, index); if (index === -1) break; var c = output[index - 1]; if (c !== '\\') { output = output.substring(0, index) + '\\' + output.substring(index); index++; } else index += 2; } } return output; } var VIEW_IF = { 'if ': 1, 'if(': 1 }; function view_parse(content, minify, filename, controller) { content = removeComments(content).ROOT(); var nocompressHTML = false; var nocompressJS = false; var nocompressCSS = false; content = content.replace(REG_NOCOMPRESS, function(text) { var index = text.lastIndexOf(' '); if (index === -1) return ''; switch (text.substring(index, text.length - 1).trim()) { case 'all': nocompressHTML = true; nocompressJS = true; nocompressCSS = true; break; case 'html': nocompressHTML = true; break; case 'js': case 'script': case 'javascript': nocompressJS = true; break; case 'css': case 'style': nocompressCSS = true; break; } return ''; }).trim(); if (!nocompressJS) content = compressJS(content, 0, filename, true); if (!nocompressCSS) content = compressCSS(content, 0, filename, true); content = F.$versionprepare(content); if (!nocompressHTML) content = compressView(content, minify, filename); var DELIMITER = '\''; var SPACE = ' '; var builder = 'var $EMPTY=\'\';var $length=0;var $source=null;var $tmp=index;var $output=$EMPTY'; var command = view_find_command(content, 0); var isFirst = false; var txtindex = -1; var index = 0; var isCookie = false; function escaper(value) { var is = REG_TAGREMOVE.test(value); if (!nocompressHTML) { // value = compressHTML(value, minify, true); } else if (!isFirst) { isFirst = true; value = value.replace(/^\s+/, ''); } if (!value) return '$EMPTY'; if (!nocompressHTML && is) value += ' '; txtindex = $VIEWCACHE.indexOf(value); if (txtindex === -1) { txtindex = $VIEWCACHE.length; $VIEWCACHE.push(value); } return '$VIEWCACHE[' + txtindex + ']'; } if (!command) builder += '+' + escaper(content); index = 0; var old = null; var newCommand = ''; var tmp = ''; var counter = 0; var functions = []; var functionsName = []; var isFN = false; var isSECTION = false; var isCOMPILATION = false; var builderTMP = ''; var sectionName = ''; var components = {}; var text; while (command) { if (!isCookie && command.command.indexOf('cookie') !== -1) isCookie = true; if (old) { text = content.substring(old.end + 1, command.beg); if (text) { if (view_parse_plus(builder)) builder += '+'; builder += escaper(text); } } else { text = content.substring(0, command.beg); if (text) { if (view_parse_plus(builder)) builder += '+'; builder += escaper(text); } } var cmd = content.substring(command.beg + 2, command.end).trim(); var cmd8 = cmd.substring(0, 8); var cmd7 = cmd.substring(0, 7); if (cmd === 'continue' || cmd === 'break') { builder += ';' + cmd + ';'; old = command; command = view_find_command(content, command.end); continue; } // cmd = cmd.replace command.command = command.command.replace(REG_HELPERS, function(text) { var index = text.indexOf('('); return index === - 1 ? text : text.substring(0, index) + '.call(self' + (text.endsWith('()') ? ')' : ',' + text.substring(index + 1)); }); if (cmd[0] === '\'' || cmd[0] === '"') { if (cmd[1] === '%') { var t = CONF[cmd.substring(2, cmd.length - 1)]; if (t != null) builder += '+' + DELIMITER + (t + '').encode().replace(/'/g, "\\'") + DELIMITER; } else builder += '+' + DELIMITER + (new Function('self', 'return self.$import(' + cmd[0] + '!' + cmd.substring(1) + ')'))(controller) + DELIMITER; } else if (cmd7 === 'compile' && cmd.lastIndexOf(')') === -1) { builderTMP = builder + '+(DEF.onCompileView.call(self,\'' + (cmd8[7] === ' ' ? cmd.substring(8).trim() : '') + '\','; builder = ''; sectionName = cmd.substring(8); isCOMPILATION = true; isFN = true; } else if (cmd8 === 'section ' && cmd.lastIndexOf(')') === -1) { builderTMP = builder; builder = '+(function(){var $output=$EMPTY'; sectionName = cmd.substring(8); isSECTION = true; isFN = true; } else if (cmd7 === 'helper ') { builderTMP = builder; builder = 'function ' + cmd.substring(7).trim() + '{var $output=$EMPTY'; isFN = true; functionsName.push(cmd.substring(7, cmd.indexOf('(', 7)).trim()); } else if (cmd8 === 'foreach ') { counter++; if (cmd.indexOf('foreach var ') !== -1) cmd = cmd.replace(' var ', SPACE); cmd = view_prepare_keywords(cmd); newCommand = (cmd.substring(8, cmd.indexOf(SPACE, 8)) || '').trim(); index = cmd.trim().indexOf(SPACE, newCommand.length + 10); if (index === -1) index = cmd.indexOf('[', newCommand.length + 10); builder += '+(function(){var $source=' + cmd.substring(index).trim() + ';if(!($source instanceof Array))$source=framework_utils.ObjectToArray($source);if(!$source.length)return $EMPTY;var $length=$source.length;var $output=$EMPTY;var index=0;for(var $i=0;$i<$length;$i++){index=$i;var ' + newCommand + '=$source[$i];$output+=$EMPTY'; } else if (cmd === 'end') { if (isFN && counter <= 0) { counter = 0; if (isCOMPILATION) { builder = builderTMP + 'unescape($EMPTY' + builder + '),model) || $EMPTY)'; builderTMP = ''; } else if (isSECTION) { builder = builderTMP + builder + ';repository[\'$section_' + sectionName + '\']=repository[\'$section_' + sectionName + '\']?repository[\'$section_' + sectionName + '\']+$output:$output;return $EMPTY})()'; builderTMP = ''; } else { builder += ';return $output;}'; functions.push(builder); builder = builderTMP; builderTMP = ''; } isSECTION = false; isCOMPILATION = false; isFN = false; } else { counter--; builder += '}return $output})()'; newCommand = ''; } } else if (VIEW_IF[cmd.substring(0, 3)]) { builder += ';if (' + (cmd.substring(2, 3) === '(' ? '(' : '') + view_prepare_keywords(cmd).substring(3) + '){$output+=$EMPTY'; } else if (cmd7 === 'else if') { builder += '} else if (' + view_prepare_keywords(cmd).substring(7) + ') {$output+=$EMPTY'; } else if (cmd === 'else') { builder += '} else {$output+=$EMPTY'; } else if (cmd === 'endif' || cmd === 'fi') { builder += '}$output+=$EMPTY'; } else { tmp = view_prepare(command.command, newCommand, functionsName, controller, components); var can = false; // Inline rendering is supported only in release mode if (RELEASE && tmp.indexOf('+') === -1 && REG_SKIP_1.test(tmp) && !REG_SKIP_2.test(tmp)) { for (var a = 0, al = RENDERNOW.length; a < al; a++) { if (tmp.startsWith(RENDERNOW[a])) { if (!a) { var isMeta = tmp.indexOf('\'meta\'') !== -1; var isHead = tmp.indexOf('\'head\'') !== -1; tmp = tmp.replace(/(\s)?'(meta|head)'(\s|,)?/g, '').replace(/(,,|,\)|\s{2,})/g, ''); if (isMeta || isHead) { var tmpimp = ''; if (isMeta) tmpimp += (isMeta ? '\'meta\'' : ''); if (isHead) tmpimp += (tmpimp ? ',' : '') + (isHead ? '\'head\'' : ''); if (tmpimp) builder += '+self.$import(' + tmpimp + ')'; } } if (tmp !== 'self.$import()') can = true; break; } } } if (can && !counter) { try { if (tmp.lastIndexOf(')') === -1) tmp += ')'; var r = (new Function('self', 'config', 'return ' + tmp))(controller, CONF).replace(REG_7, '\\\\').replace(REG_8, '\\\''); if (r) { txtindex = $VIEWCACHE.indexOf(r); if (txtindex === -1) { txtindex = $VIEWCACHE.length; $VIEWCACHE.push(r); } builder += '+$VIEWCACHE[' + txtindex + ']'; } } catch (e) { console.log('A view compilation error --->', filename, e, tmp); F.errors.push({ error: e.stack, name: filename, url: null, date: new Date() }); if (view_parse_plus(builder)) builder += '+'; builder += wrapTryCatch(tmp, command.command, command.line); } } else if (tmp) { if (view_parse_plus(builder)) builder += '+'; if (tmp.substring(1, 4) !== '@{-' && tmp.substring(0, 11) !== 'self.$view') builder += wrapTryCatch(tmp, command.command, command.line); else builder += tmp; } } old = command; command = view_find_command(content, command.end); } if (old) { text = content.substring(old.end + 1); if (text) builder += '+' + escaper(text); } if (RELEASE) builder = builder.replace(/(\+\$EMPTY\+)/g, '+').replace(/(\$output=\$EMPTY\+)/g, '$output=').replace(/(\$output\+=\$EMPTY\+)/g, '$output+=').replace(/(\}\$output\+=\$EMPTY)/g, '}').replace(/(\{\$output\+=\$EMPTY;)/g, '{').replace(/(\+\$EMPTY\+)/g, '+').replace(/(>'\+'<)/g, '><').replace(/'\+'/g, ''); var keys = Object.keys(components); builder = builder.replace(REG_VIEW_PART, function(text) { var data = []; var comkeys = Object.keys(F.components.instances); var key = text.substring(6, text.length - 2); for (var i = 0; i < comkeys.length; i++) { var com = F.components.instances[comkeys[i]]; if (com.parts && com.group && components[com.group] && com.parts[key]) data.push(com.parts[key]); } if (!data.length) return '$EMPTY'; data = data.join(''); var index = $VIEWCACHE.indexOf(data); if (index === -1) index = $VIEWCACHE.push(data) - 1; return '$VIEWCACHE[' + index + ']'; }); var fn = ('(function(self,repository,model,session,query,body,url,helpers,user,config,functions,index,output,files,mobile,settings){var R=this.repository;var M=model;var theme=this.themeName;var language=this.req.$language||\'\';var sitemap=this.repository.$sitemap;' + (isCookie ? 'var cookie=function(name){return self.req.cookie(name)};' : '') + (functions.length ? functions.join('') + ';' : '') + 'var controller=self;' + builder + ';return $output;})'); try { fn = eval(fn); fn.components = keys; } catch (e) { throw new Error(filename + ': ' + (e.message + '')); } return fn; } function view_prepare_keywords(cmd) { return cmd.replace(REG_SITEMAP, text => ' self.' + text.trim()); } function wrapTryCatch(value, command, line) { return DEBUG ? ('(function(){try{return ' + value + '}catch(e){throw new Error(unescape(\'' + escape(command) + '\') + \' - Line: ' + line + ' - \' + e.message.toString());}return $EMPTY})()') : value; } function view_parse_plus(builder) { var c = builder[builder.length - 1]; return c !== '!' && c !== '?' && c !== '+' && c !== '.' && c !== ':'; } function view_prepare(command, dynamicCommand, functions, controller, components) { var a = command.indexOf('.'); var b = command.indexOf('('); var c = command.indexOf('['); var max = []; var tmp = 0; if (a !== -1) max.push(a); if (b !== -1) max.push(b); if (c !== -1) max.push(c); var index = Math.min.apply(this, max); if (index === -1) index = command.length; var name = command.substring(0, index); if (name === dynamicCommand) return '$STRING(' + command + ', 1)'; if (name[0] === '!' && name.substring(1) === dynamicCommand) return '$STRING(' + command.substring(1) + ')'; switch (name) { case 'foreach': case 'end': return ''; case 'part': tmp = command.indexOf('('); return '/*PART{0}*/'.format(command.substring(tmp + 2, command.length - 2)); case 'section': tmp = command.indexOf('('); return tmp === -1 ? '' : '(repository[\'$section_' + command.substring(tmp + 1, command.length - 1).replace(/'|"/g, '') + '\'] || \'\')'; case 'log': case 'LOG': return '(' + (name === 'log' ? 'F.' : '') + command + '?$EMPTY:$EMPTY)'; case 'logger': case 'LOGGER': return '(' + (name === 'logger' ? 'F.' : '') + command + '?$EMPTY:$EMPTY)'; case 'console': return '(' + command + '?$EMPTY:$EMPTY)'; case '!cookie': return '$STRING(' + command + ')'; case 'csrf': return 'self.csrf()'; case 'root': var r = CONF.default_root; return '\'' + (r ? r.substring(0, r.length - 1) : r) + '\''; case 'M': case 'R': case 'G': case 'model': case 'repository': case 'query': case 'global': case 'MAIN': case 'session': case 'user': case 'config': case 'CONF': case 'REPO': case 'controller': return view_is_assign(command) ? ('self.$set(' + command + ')') : ('$STRING(' + command + ', 1)'); case 'body': return view_is_assign(command) ? ('self.$set(' + command + ')') : command.lastIndexOf('.') === -1 ? 'output' : ('$STRING(' + command + ', 1)'); case 'files': case 'mobile': case 'continue': case 'break': case 'language': case 'TRANSLATE': case 'helpers': return command; case 'cookie': case 'settings': case 'CONFIG': case 'FUNC': case 'function': case 'MODEL': case 'SCHEMA': case 'MODULE': case 'functions': return '$STRING(' + command + ', 1)'; case '!M': case '!R': case '!G': case '!controller': case '!repository': case '!get': case '!post': case '!body': case '!query': case '!global': case '!session': case '!user': case '!config': case '!CONF': case '!functions': case '!model': case '!CONFIG': case '!SCHEMA': case '!function': case '!MODEL': case '!MODULE': return '$STRING(' + command.substring(1) + ')'; case 'resource': return '$STRING(RESOURCE' + command.substring(8) + ', 1)'; case 'RESOURCE': return '$STRING(' + command + ', 1)'; case '!resource': return '$STRING(self.' + command.substring(1) + ')'; case '!RESOURCE': return '$STRING(' + command.substring(1) + ')'; case 'host': case 'hostname': return command.indexOf('(') === -1 ? 'self.host()' : 'self.' + command; case 'href': return command.indexOf('(') === -1 ? 'self.href()' : 'self.' + command; case 'url': return command.indexOf('(') === -1 ? 'self.' + command : 'self.$' + command; case 'title': case 'description': case 'keywords': case 'author': return command.indexOf('(') === -1 ? '((repository[\'$' + command + '\'] || \'\') + \'\').encode()' : 'self.$' + command; case 'title2': return 'self.$' + command; case '!title': case '!description': case '!keywords': case '!author': return '(repository[\'$' + command.substring(1) + '\'] || \'\')'; case 'head': return command.indexOf('(') === -1 ? 'self.' + command + '()' : 'self.$' + command; case 'sitemap_url': case 'sitemap_name': case 'sitemap_navigation': case 'sitemap_url2': case 'sitemap_name2': return 'self.' + command; case 'breadcrumb_url': case 'breadcrumb_name': case 'breadcrumb_url2': case 'breadcrumb_name2': case 'breadcrumb_navigation': return 'self.sitemap_' + command.substring(10); case 'sitemap': case 'breadcrumb': case 'place': if (name === 'breadcrumb') name = 'sitemap'; return command.indexOf('(') === -1 ? '(repository[\'$' + command + '\'] || \'\')' : 'self.$' + command; case 'meta': return command.indexOf('(') === -1 ? 'self.$meta()' : 'self.$' + command; case 'import': case 'favicon': case 'js': case 'css': return 'self.$' + command + (command.indexOf('(') === -1 ? '()' : ''); case 'components': var group = command.match(REG_COMPONENTS_GROUP); if (group && group.length) { group = (group[0] + '').replace(/'|"'/g, ''); components[group] = 1; } return 'self.$' + command + (command.indexOf('(') === -1 ? '()' : ''); case 'index': return '(' + command + ')'; case 'component': tmp = command.indexOf('\''); var is = false; if (tmp !== -1) { name = command.substring(tmp + 1, command.indexOf('\'', tmp + 1)); tmp = F.components.instances[name]; if (tmp && tmp.render) is = true; } else { tmp = command.indexOf('"'); name = command.substring(tmp + 1, command.indexOf('"', tmp + 1)); tmp = F.components.instances[name]; if (tmp && tmp.render) is = true; } if (tmp) components[tmp.group] = 1; if (is) { var settings = command.substring(11 + name.length + 2, command.length - 1).trim(); if (settings === ')') settings = ''; $VIEWASYNC++; return '\'@{-{0}-}\'+(function(index){!controller.$viewasync&&(controller.$viewasync=[]);controller.$viewasync.push({replace:\'@{-{0}-}\',name:\'{1}\',settings:{2}});return $EMPTY})({0})'.format($VIEWASYNC, name, settings || 'null'); } return 'self.' + command; case 'public_js': case 'public_css': case 'public_image': case 'public_font': case 'public_download': case 'public_video': case 'public': case 'translate': return 'self.' + command; case 'json': case 'json2': case 'sitemap_change': case 'sitemap_replace': case 'sitemap_add': case 'helper': case 'view': case 'layout': case 'image': case 'view_compile': case 'download': case 'selected': case 'disabled': case 'checked': case 'header': case 'options': case 'readonly': return 'self.$' + command; case 'now': return '(new Date()' + command.substring(3) + ')'; case 'radio': case 'text': case 'input': case 'checkbox': case 'hidden': case 'textarea': case 'password': return 'self.$' + appendModel(command); default: return DEF.helpers[name] ? ('helpers.' + view_insert_call(command)) : ('$STRING(' + (functions.indexOf(name) === -1 ? command[0] === '!' ? command.substring(1) + ')' : command + ', 1)' : command + ')')); } } function view_insert_call(command) { var beg = command.indexOf('('); if (beg === -1) return command; var length = command.length; var count = 0; for (var i = beg + 1; i < length; i++) { var c = command[i]; if (c !== '(' && c !== ')') continue; if (c === '(') { count++; continue; } if (count > 0) { count--; continue; } var arg = command.substring(beg + 1); return command.substring(0, beg) + '.call(self' + (arg.length > 1 ? ',' + arg : ')'); } return command; } function view_is_assign(value) { var length = value.length; var skip = 0; for (var i = 0; i < length; i++) { var c = value[i]; if (c === '[') { skip++; continue; } if (c === ']') { skip--; continue; } var next = value[i + 1] || ''; if (c === '+' && (next === '+' || next === '=')) { if (!skip) return true; } if (c === '-' && (next === '-' || next === '=')) { if (!skip) return true; } if (c === '*' && (next === '*' || next === '=')) { if (!skip) return true; } if (c === '=') { if (!skip) return true; } } return false; } function view_find_command(content, index, entire) { index = content.indexOf('@{', index); if (index === -1) return null; var length = content.length; var count = 0; for (var i = index + 2; i < length; i++) { var c = content[i]; if (c === '{') { count++; continue; } if (c !== '}') continue; else if (count > 0) { count--; continue; } var command = content.substring(index + 2, i).trim(); // @{{ SKIP }} if (command[0] === '{') return view_find_command(content, index + 1); var obj = { beg: index, end: i, line: view_line_counter(content.substr(0, index)), command: command }; if (entire) obj.phrase = content.substring(index, i + 1); return obj; } return null; } function view_line_counter(value) { var count = value.match(/\n/g); return count ? count.length : 0; } function view_find_localization(content, index) { index = content.indexOf('@(', index); if (index === -1) return null; var length = content.length; var count = 0; var beg = content[index - 1]; var esc = ''; for (var i = index + 2; i < length; i++) { var c = content[i]; if (c === '(') { count++; continue; } if (c !== ')') continue; else if (count) { count--; continue; } var end = content.substring(i + 1, i + 2); if (beg === end && beg === '"' || beg === '\'' || beg === '`') esc = beg; return { beg: index, end: i, command: content.substring(index + 2, i).trim(), escape: esc }; } return null; } function removeComments(html) { var tagBeg = '<!--'; var tagEnd = '-->'; var beg = html.indexOf(tagBeg); var end = 0; while (beg !== -1) { end = html.indexOf(tagEnd, beg + 4); if (end === -1) break; var comment = html.substring(beg, end + 3); if (comment.indexOf('[if') !== -1 || comment.indexOf('[endif') !== -1) { beg = html.indexOf(tagBeg, end + 3); } else { html = html.replacer(comment, ''); beg = html.indexOf(tagBeg, beg); } } return html; } function compressView(html, minify) { var cache = []; var beg = 0; var end; while (true) { beg = html.indexOf('@{compile ', beg - 1); if (beg === -1) break; end = html.indexOf('@{end}', beg + 6); if (end === -1) break; cache.push(html.substring(beg, end + 6)); html = html.substring(0, beg) + '#@' + (cache.length - 1) + '#' + html.substring(end + 6); } while (true) { beg = html.indexOf('@{', beg); if (beg === -1) break; end = html.indexOf('}', beg + 2); if (end === -1) break; cache.push(html.substring(beg, end + 1)); html = html.substring(0, beg) + '#@' + (cache.length - 1) + '#' + html.substring(end + 1); } html = compressHTML(html, minify, false); return html.replace(/#@\d+#/g, function(text) { return cache[+text.substring(2, text.length - 1)]; }); } /** * Inline JS compressor * @private * @param {String} html HTML. * @param {Number} index Last index. * @return {String} */ function compressJS(html, index, filename, nomarkup) { if (!CONF.allow_compile_script) return html; var strFrom = '<script type="text/javascript">'; var strTo = '</script>'; var indexBeg = html.indexOf(strFrom, index || 0); if (indexBeg === -1) { strFrom = '<script>'; indexBeg = html.indexOf(strFrom, index || 0); if (indexBeg === -1) return html; } var indexEnd = html.indexOf(strTo, indexBeg + strFrom.length); if (indexEnd === -1) return html; var js = html.substring(indexBeg, indexEnd + strTo.length).trim(); var beg = html.indexOf(js); if (beg === -1) return html; var val = js.substring(strFrom.length, js.length - strTo.length).trim(); var compiled = exports.compile_javascript(val, filename, nomarkup); html = html.replacer(js, strFrom + compiled.trim() + strTo.trim()); return compressJS(html, indexBeg + compiled.length + 9, filename, nomarkup); } function compressCSS(html, index, filename, nomarkup) { if (!CONF.allow_compile_style) return html; var strFrom = '<style'; var strTo = '</style>'; var indexBeg = html.indexOf(strFrom, index || 0); if (indexBeg === -1) return html; var indexBeg2 = html.indexOf('>', indexBeg + strFrom.length); if (indexBeg2 === -1) return html; strFrom = html.substring(indexBeg, indexBeg2 + 1); var indexEnd = html.indexOf(strTo, indexBeg2 + strFrom.length); if (indexEnd === -1) return html; var css = html.substring(indexBeg, indexEnd + strTo.length); var val = css.substring(strFrom.length, css.length - strTo.length).trim(); var compiled = exports.compile_css(val, filename, nomarkup); html = html.replacer(css, (strFrom + compiled.trim() + strTo).trim()); return compressCSS(html, indexBeg2 + compiled.length + 8, filename, nomarkup); } function variablesCSS(content) { if (!content) return content; var variables = {}; content = content.replace(REG_CSS_10, function(text) { var index = text.indexOf(':'); if (index === -1) return text; var key = text.substring(0, index).trim(); variables[key] = text.substring(index + 1, text.length - 1).trim(); return ''; }); content = content.replace(REG_CSS_11, function(text) { var index = text.indexOf('||'); var variable = ''; var last = text[text.length - 1]; var len = text.length; if (last === ';' || last === '}' || last === '!' || last === ' ') len = len - 1; else last = ''; if (index !== -1) variable = variables[text.substring(0, index).trim()] || text.substring(index + 2, len).trim(); else variable = variables[text.substring(0, len).trim()]; return variable ? (variable + last) : text; }).trim(); return content; } function nested(css, id, variable) { if (!css) return css; var index = 0; var output = ''; var A = false; var count = 0; var beg; var begAt; var valid = false; var plus = ''; var skip = false; var skipImport = ''; var isComment = false; var comment = ''; var skipView = false; var skipType; while (true) { var a = css[index++]; if (!a) break; if (a === '/' && css[index] === '*') { isComment = true; index++; comment = ''; continue; } if (isComment) { comment += a; if (a === '*' && css[index] === '/') { isComment = false; index++; if (comment === 'auto*') output += '/*auto*/'; } continue; } if (a === '\n' || a === '\r') continue; if (a === '$' && variable) variable(); if (a === '@' && css[index] === '{') skipView = true; if (skipView) { plus += a; if (a === '}') skipView = false; continue; } if (a === '\'' || a === '"') { if (a === skipType && css[index] !== '\\') skipType = ''; else if (!skipType) { skipType = a; } } if (skipType) { plus += a; continue; } if (a === '@') { begAt = index; skip = true; } if (skip && !skipImport && (a === ';' || a === '{')) { skipImport = a; if (a === ';') { output += css.substring(begAt - 1, index); skip = false; plus = ''; continue; } } plus += a; if (a === '{') { if (A) { count++; continue; } A = true; count = 0; beg = index; valid = false; continue; } if (a === '}') { if (count > 0) { count--; valid = true; continue; } if (!valid) { output += plus; plus = ''; A = false; skip = false; skipImport = ''; continue; } if (skip) { if (plus.indexOf('@keyframes') !== -1) { output += plus; } else { begAt = plus.indexOf('{'); output += plus.substring(0, begAt + 1) + process_nested(plus.substring(begAt), id).trim() + '}'; } A = false; skip = false; skipImport = ''; plus = ''; continue; } var ni = beg - 1; var name = ''; while (true) { var b = css[ni--]; if (b === '{') continue; if (b === '}' || b === '\n' || b === '\r' || b === undefined || (skipImport && skipImport === b)) break; name = b + name; } A = false; skip = false; skipImport = ''; plus = ''; output += process_nested(css.substring(beg - 1, index), (id || '') + name.trim()); } } return output + plus; } function process_nested(css, name) { css = css.trim(); css = make_nested(css.substring(1, css.length - 1), name); return nested(css, name); } function make_nested(css, name) { var index = 0; var plus = ''; var output = ''; var count = 0; var A = false; var valid = false; while (true) { var a = css[index++]; if (!a) break; if (a === '\n' || a === '\r') continue; if (a !== ' ' || plus[plus.length -1] !== ' ') plus += a; if (a === '{') { if (A) { count++; continue; } A = true; count = 0; valid = false; continue; } if (a === '}') { if (count > 0) { count--; valid = true; continue; } if (!valid) { output += name + ' ' + plus.trim(); plus = ''; A = false; continue; } output += plus; } } return output; } function compressHTML(html, minify, isChunk) { if (!html || !minify) return html; html = removeComments(html.replace(REG_WIN, '')); var tags = ['script', 'textarea', 'pre', 'code', 'readme']; var id = '[' + new Date().getTime() + ']#'; var cache = {}; var indexer = 0; var length = tags.length; var chars = 65; var tagBeg, tagEnd, beg, end, len, key, value; for (var i = 0; i < length; i++) { var o = tags[i]; tagBeg = '<' + o; tagEnd = '</' + o; beg = html.indexOf(tagBeg); end = 0; len = tagEnd.length; while (beg !== -1) { end = html.indexOf(tagEnd, beg + 3); if (end === -1) { if (isChunk) end = html.length; else break; } key = id + (indexer++) + String.fromCharCode(chars++); if (chars > 90) chars = 65; value = html.substring(beg, end + len); if (!i) { end = value.indexOf('>'); len = value.indexOf('type="text/template"'); if (len < end && len !== -1) { beg = html.indexOf(tagBeg, beg + tagBeg.length); continue; } len = value.indexOf('type="text/html"'); if (len < end && len !== -1) { beg = html.indexOf(tagBeg, beg + tagBeg.length); continue; } len = value.indexOf('type="text/ng-template"'); if (len < end && len !== -1) { beg = html.indexOf(tagBeg, beg + tagBeg.length); continue; } } cache[key] = value; html = html.replacer(value, key); beg = html.indexOf(tagBeg, beg + tagBeg.length); } } while (true) { if (!REG_6.test(html)) break; html = html.replace(REG_6, text => text.replace(/\s+/g, ' ')); } html = html.replace(REG_9, '>').replace(REG_10, function(text) { return text.trim().replace(/\s/g, ''); }).replace(REG_5, '><').replace(REG_4, function(text) { var c = text[text.length - 1]; return c === '<' ? c : ' ' + c; }).replace(REG_1, '').replace(REG_2, ''); for (var k in cache) html = html.replacer(k, cache[k]); return html; } /** * Read file * @param {String} path * @return {Object} */ function viewengine_read(path, controller) { var config = CONF; var out = path[0] === '.'; var filename = out ? path.substring(1) : PATH.views(path); var key; if (RELEASE) { key = '404/' + path; var is = F.temporary.other[key]; if (is !== undefined) return null; } if (existsSync(filename)) return view_parse(view_parse_localization(modificators(Fs.readFileSync(filename).toString('utf8'), filename, 'view', controller), controller.language), config.allow_compile_html, filename, controller); var index; if (out) { if (controller.themeName) { index = filename.lastIndexOf('/'); if (index !== -1) { filename = filename.substring(0, filename.lastIndexOf('/', index - 1)) + filename.substring(index); if (existsSync(filename)) return view_parse(view_parse_localization(modificators(Fs.readFileSync(filename).toString('utf8'), filename, 'view', controller), controller.language), config.allow_compile_html, filename, controller); } } if (RELEASE) F.temporary.other[key] = null; return null; } index = path.lastIndexOf('/'); if (index === -1) { if (RELEASE) F.temporary.other[key] = null; return null; } filename = PATH.views(path.substring(index + 1)); if (existsSync(filename)) return view_parse(view_parse_localization(modificators(Fs.readFileSync(filename).toString('utf8'), filename, 'view', controller), controller.language), config.allow_compile_html, filename, controller); if (RELEASE) F.temporary.other[key] = null; return null; } function modificators(value, filename, type, controller) { if (filename) { var length = F.directory.length; if (filename.substring(0, length) === F.directory) { filename = filename.substring(length); if (filename[0] !== '/') filename = '/' + filename; } if (F.modificators2) { var arr = F.modificators2[filename]; if (arr) { for (var i = 0; i < arr.length; i++) { var output = arr[i](type || 'view', filename, value, controller); if (output)