diff --git a/script/webpack.base.js b/script/webpack.base.js
index 6be7ca4..ed77891 100644
--- a/script/webpack.base.js
+++ b/script/webpack.base.js
@@ -6,10 +6,11 @@ const path = require('path')
process.traceDeprecation = true
-let nodeModDir = path.resolve('./node_modules/') + '/'
-let banner = pkg.name + ' v' + pkg.version + ' ' + pkg.homepage
+const nodeModDir = path.resolve('./node_modules/') + '/'
+const srcDir = path.resolve('./src') + '/'
+const banner = pkg.name + ' v' + pkg.version + ' ' + pkg.homepage
-let postcssLoader = {
+const postcssLoader = {
loader: 'postcss-loader',
options: {
plugins: [classPrefix('eruda-'), autoprefixer]
@@ -68,7 +69,7 @@ module.exports = {
test: /\.hbs$/,
loader: nodeModDir + 'handlebars-loader/index.js',
options: {
- runtime: nodeModDir + 'handlebars/dist/handlebars.runtime.js'
+ runtime: srcDir + 'lib/handlebars.js'
}
},
{
diff --git a/src/Console/Console.js b/src/Console/Console.js
index d156c2d..fa08d32 100644
--- a/src/Console/Console.js
+++ b/src/Console/Console.js
@@ -45,7 +45,12 @@ export default class Console extends Tool {
}
winConsole[name] = (...args) => {
- if (args.length > 0) {
+ if (
+ args.length > 0 ||
+ name === 'group' ||
+ name === 'groupCollapsed' ||
+ name === 'groupEnd'
+ ) {
this[name](...args)
}
origin(...args)
@@ -325,5 +330,8 @@ const CONSOLE_METHOD = [
'table',
'assert',
'count',
- 'debug'
+ 'debug',
+ 'group',
+ 'groupCollapsed',
+ 'groupEnd'
]
diff --git a/src/Console/Log.hbs b/src/Console/Log.hbs
index a7300a4..1d9711b 100644
--- a/src/Console/Log.hbs
+++ b/src/Console/Log.hbs
@@ -5,12 +5,17 @@
{{/if}}
+ {{#repeat group.indentLevel}}
+
+ {{/repeat}}
{{#if icon}}
{{/if}}
-
+
diff --git a/src/Console/Log.js b/src/Console/Log.js
index 6afb639..9616f21 100644
--- a/src/Console/Log.js
+++ b/src/Console/Log.js
@@ -33,10 +33,14 @@ export default class Log {
type = 'log',
args = [],
id,
+ group = {},
+ targetGroup = {},
displayHeader = false,
ignoreFilter = false
}) {
this.type = type
+ this.group = group
+ this.targetGroup = targetGroup
this.args = args
this.count = 1
this.id = id
@@ -53,26 +57,48 @@ export default class Log {
addCount() {
this.count++
const count = this.count
- let msg = this.formattedMsg
- if (count === 2)
- msg = msg.replace('eruda-count eruda-hidden', 'eruda-count')
+ let msg = this._formattedMsg
+ if (count === 2) {
+ msg = msg.replace(
+ 'eruda-count-container eruda-hidden',
+ 'eruda-count-container'
+ )
+ }
msg = msg.replace(/data-mark="count">\d*/, 'data-mark="count">' + count)
+ msg = msg.replace(
+ 'class="eruda-icon-container"',
+ 'class="eruda-icon-container eruda-hidden"'
+ )
- this.formattedMsg = msg
+ this._formattedMsg = msg
+
+ return this
+ }
+ groupEnd() {
+ let msg = this._formattedMsg
+
+ const mark = '"eruda-nesting-level"'
+ const lastIdx = msg.lastIndexOf(mark)
+ const len = lastIdx + mark.length - 1
+ msg = msg.slice(0, len) + ' eruda-group-closed"' + msg.slice(len)
+ this._formattedMsg = msg
return this
}
updateTime(time) {
- let msg = this.formattedMsg
+ let msg = this._formattedMsg
if (this.time) {
msg = msg.replace(/data-mark="time">(.*?), `data-mark="time">${time}<`)
this.time = time
- this.formattedMsg = msg
+ this._formattedMsg = msg
}
return this
}
+ content() {
+ return this._formattedMsg
+ }
_needSrc() {
const { type, args } = this
@@ -103,7 +129,7 @@ export default class Log {
}
_formatMsg() {
let { args } = this
- const { type, id, displayHeader, time, from } = this
+ const { type, id, displayHeader, time, from, group } = this
// Don't change original args for lazy evaluation.
args = clone(args)
@@ -156,16 +182,19 @@ export default class Log {
msg = formatMsg(args)
icon = 'arrow-left'
break
+ case 'group':
+ msg = formatMsg(args.length === 0 ? 'console.group' : args)
+ break
}
if (type !== 'error') msg = recognizeUrl(msg)
this.value = msg
- msg = render({ msg, type, icon, id, displayHeader, time, from })
+ msg = render({ msg, type, icon, id, displayHeader, time, from, group })
if (!this._needSrc() || !Log.lazyEvaluation) {
delete this.args
}
- this.formattedMsg = msg
+ this._formattedMsg = msg
}
static click(type, log, $el, logger) {
switch (type) {
diff --git a/src/Console/Logger.js b/src/Console/Logger.js
index e20d110..2d97abe 100644
--- a/src/Console/Logger.js
+++ b/src/Console/Logger.js
@@ -14,7 +14,8 @@ import {
isFn,
stripHtmlTag,
loadJs,
- $
+ $,
+ Stack
} from '../lib/util'
export default class Logger extends Emitter {
@@ -31,6 +32,7 @@ export default class Logger extends Emitter {
this._filter = 'all'
this._maxNum = 'infinite'
this._displayHeader = false
+ this._groupStack = new Stack()
this._bindEvent()
}
@@ -134,6 +136,22 @@ export default class Logger extends Emitter {
warn(...args) {
return this.insert('warn', args)
}
+ group(...args) {
+ return this.insert('group', args)
+ }
+ groupCollapsed() {}
+ groupEnd() {
+ const lastLog = this._lastLog
+ const $el = this._$el
+ lastLog.groupEnd()
+ this._groupStack.pop()
+
+ const $container = $el.find(`div[data-id="${lastLog.id}"]`)
+ if ($container.length > 0) {
+ $container.parent().remove()
+ $el.append(lastLog.content())
+ }
+ }
input(jsCode) {
if (startWith(jsCode, ':')) {
this._runCmd(jsCode.slice(1))
@@ -185,7 +203,7 @@ export default class Logger extends Emitter {
logs = this._filterLogs(logs)
for (let i = 0, len = logs.length; i < len; i++) {
- html += logs[i].formattedMsg
+ html += logs[i].content()
}
this._$el.html(html)
@@ -196,16 +214,31 @@ export default class Logger extends Emitter {
insert(type, args) {
const logs = this._logs
const $el = this._$el
+ const groupStack = this._groupStack
const el = $el.get(0)
const isAtBottom = el.scrollTop === el.scrollHeight - el.offsetHeight
const options = isStr(type) ? { type, args } : type
+ if (groupStack.size > 0) {
+ options.group = groupStack.peek()
+ }
extend(options, {
id: uniqId('log'),
displayHeader: this._displayHeader
})
+ if (type === 'group') {
+ const group = {
+ id: uniqId('group'),
+ collapsed: false,
+ parent: groupStack.peek(),
+ indentLevel: groupStack.size + 1
+ }
+ options.targetGroup = group
+ groupStack.push(group)
+ }
+
let log = new Log(options)
const lastLog = this._lastLog
@@ -216,15 +249,12 @@ export default class Logger extends Emitter {
!log.src &&
!log.args
) {
+ lastLog.addCount()
+ if (log.time) lastLog.updateTime(log.time)
+ log = lastLog
const $container = $el.find(`div[data-id="${lastLog.id}"]`)
if ($container.length > 0) {
- lastLog.addCount()
- if (log.time) lastLog.updateTime(log.time)
$container.parent().remove()
- log = lastLog
- } else {
- logs.push(log)
- this._lastLog = log
}
} else {
logs.push(log)
@@ -241,7 +271,7 @@ export default class Logger extends Emitter {
}
if (this._filterLog(log) && this._container.active) {
- $el.append(log.formattedMsg)
+ $el.append(log.content())
}
this.emit('insert', log)
@@ -266,7 +296,7 @@ export default class Logger extends Emitter {
return logs.filter(log => {
if (log.ignoreFilter) return true
if (isFilterFn) return filter(log)
- if (isFilterRegExp) return filter.test(stripHtmlTag(log.formattedMsg))
+ if (isFilterRegExp) return filter.test(stripHtmlTag(log.content()))
return log.type === filter
})
}
@@ -280,7 +310,7 @@ export default class Logger extends Emitter {
if (log.ignoreFilter) return true
if (isFilterFn) return filter(log)
- if (isFilterRegExp) return filter.test(stripHtmlTag(log.formattedMsg))
+ if (isFilterRegExp) return filter.test(stripHtmlTag(log.content()))
return log.type === filter
}
diff --git a/src/Console/Logger.scss b/src/Console/Logger.scss
index 12ab1fb..a699815 100644
--- a/src/Console/Logger.scss
+++ b/src/Console/Logger.scss
@@ -9,6 +9,12 @@
height: 100%;
font-size: $font-size;
padding-top: 1px;
+ & > li:last-child {
+ border-bottom: 1px solid $gray-light;
+ .log-item {
+ border-bottom-width: 1px;
+ }
+ }
.header {
@include overflow-auto(x);
white-space: nowrap;
@@ -19,21 +25,40 @@
}
.log-item {
position: relative;
+ display: flex;
@include clear-float();
background: #fff;
- padding: 3px $padding;
- border-bottom: 1px solid $gray-light;
border-top: 1px solid $gray-light;
+ border-bottom: 2px solid transparent;
margin-top: -1px;
a {
color: $blue !important;
}
- .count,
- .icon-container {
- float: left;
- margin-right: 5px;
+ .nesting-level {
+ width: 14px;
+ flex-shrink: 0;
+ margin-top: -1px;
+ margin-bottom: -2px;
+ position: relative;
+ border-right: 1px solid $gray;
+ &.group-closed::before {
+ content: '';
+ }
+ &::before {
+ border-bottom: 1px solid #a5a5a5;
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin-left: 100%;
+ width: 5px;
+ height: calc(100% - 1px);
+ box-sizing: border-box;
+ }
}
.icon-container {
+ margin-right: -6px;
+ padding-top: 3px;
+ margin: 0 -6px 0 $padding;
.icon {
line-height: 20px;
font-size: 12px;
@@ -57,11 +82,15 @@
padding: 2px 4px;
color: #fff;
border-radius: 10px;
+ float: left;
+ margin: 3px -6px 0 $padding;
}
.log-content-wrapper {
+ flex: 1;
overflow: hidden;
}
.log-content {
+ padding: 3px $padding;
@include overflow-auto(x);
white-space: pre-wrap;
user-select: text;
@@ -112,10 +141,9 @@
color: $blue;
}
&.warn {
- z-index: 40;
background: #fffbe6;
border-top: 1px solid $yellow;
- border-bottom: 1px solid $yellow;
+ border-bottom: 2px solid $yellow;
}
&.info {
z-index: 30;
@@ -124,6 +152,10 @@
&.output {
color: $gray-dark;
}
+ &.group {
+ color: #222;
+ font-weight: bold;
+ }
}
}
}
diff --git a/src/lib/handlebars.js b/src/lib/handlebars.js
new file mode 100644
index 0000000..170b206
--- /dev/null
+++ b/src/lib/handlebars.js
@@ -0,0 +1,66 @@
+const handlebars = require('handlebars/runtime')
+const isNum = require('licia/isNum')
+
+// https://github.com/helpers/handlebars-helper-repeat
+function repeatHelper() {
+ var args = arguments
+
+ if (args.length > 2) {
+ throw new Error(`Expected 0, 1 or 2 arguments, but got ${args.length}`)
+ }
+
+ var options = args[args.length - 1]
+ var hash = options.hash || {}
+ var count = hash.count || args[0] || 0
+ var start = hash.start || 0
+ var step = hash.step || 1
+ var data = { count, start, step }
+
+ if (typeof args[0] === 'string' && !isNum(args[0]) && args[0] !== '') {
+ return repeat(data, args[0])
+ }
+
+ if (data.count > 0) {
+ return repeatBlock(data, this, options)
+ }
+
+ return options.inverse(this)
+}
+
+function repeat({ count, start, step }, thisArg) {
+ var max = count * step + start
+ var index = start
+ var str = ''
+
+ while (index < max) {
+ str += thisArg
+ index += step
+ }
+ return str
+}
+
+function repeatBlock({ count, start, step }, thisArg, options) {
+ var max = count * step + start
+ var index = start
+ var str = ''
+
+ do {
+ var data = {
+ index,
+ count,
+ start,
+ step,
+ first: index === start,
+ last: index >= max - step
+ }
+ var blockParams = [index, data]
+ str += options.fn(thisArg, { data, blockParams })
+ index += data.step
+ } while (index < max)
+
+ return str
+}
+
+handlebars.registerHelper('repeat', repeatHelper)
+
+module.exports = handlebars
diff --git a/src/lib/util.js b/src/lib/util.js
index 2020e70..6f010b6 100644
--- a/src/lib/util.js
+++ b/src/lib/util.js
@@ -1,6 +1,5 @@
// Built by eustia.
/* eslint-disable */
-"use strict";
var _ = {};
@@ -1953,175 +1952,6 @@ export var extendOwn = _.extendOwn = (function (exports) {
return exports;
})({});
-/* ------------------------------ highlight ------------------------------ */
-
-export var highlight = _.highlight = (function (exports) {
- /* Highlight code.
- *
- * |Name |Type |Desc |
- * |-------|------|----------------------------|
- * |str |string|Code string |
- * |lang=js|string|Language, js, html or css |
- * |[style]|object|Keyword highlight style |
- * |return |string|Highlighted html code string|
- *
- * Available styles:
- *
- * comment, string, number, keyword, operator
- */
-
- /* example
- * highlight('const a = 5;', 'js', {
- * number: 'color:#0086b3;'
- * }); // -> '
const a
= 5;'
- */
-
- /* typescript
- * export declare namespace highlight {
- * interface IStyle {
- * comment?: string;
- * string?: string;
- * number?: string;
- * keyword?: string;
- * operator?: string;
- * }
- * }
- * export declare function highlight(
- * str: string,
- * lang?: string,
- * style: highlight.IStyle
- * ): string;
- */
-
- /* dependencies
- * each
- */ // https://github.com/trentrichardson/jQuery-Litelighter
-
- exports = function(str, lang) {
- lang = lang || 'js';
- str = str.replace(//g, '>');
- lang = language[lang];
- var subLangSi = 0;
- var subLangs = [];
- each(lang, function(val) {
- if (!val.language) return;
- str = str.replace(val.re, function($1, $2) {
- subLangs[subLangSi++] = exports($2, val.language);
- return $1.replace($2, '___subtmpl' + (subLangSi - 1) + '___');
- });
- });
- each(lang, function(val, key) {
- if (language[val.language]) return;
- str = str.replace(val.re, '___' + key + '___$1___end' + key + '___');
- });
- var levels = [];
- str = str.replace(/___(?!subtmpl)\w+?___/g, function($0) {
- var end = $0.substr(3, 3) === 'end',
- tag = (!end ? $0.substr(3) : $0.substr(6)).replace(/_/g, ''),
- lastTag = levels.length > 0 ? levels[levels.length - 1] : null;
-
- if (
- !end &&
- (lastTag == null ||
- tag == lastTag ||
- (lastTag != null &&
- lang[lastTag] &&
- lang[lastTag].embed != undefined &&
- lang[lastTag].embed.indexOf(tag) > -1))
- ) {
- levels.push(tag);
- return $0;
- } else if (end && tag == lastTag) {
- levels.pop();
- return $0;
- }
-
- return '';
- });
- each(lang, function(val, key) {
- str = str
- .replace(new RegExp('___end' + key + '___', 'g'), '')
- .replace(
- new RegExp('___' + key + '___', 'g'),
- '
'
- );
- });
- each(lang, function(val) {
- if (!val.language) return;
- str = str.replace(/___subtmpl\d+___/g, function($tmpl) {
- var i = parseInt($tmpl.replace(/___subtmpl(\d+)___/, '$1'), 10);
- return subLangs[i];
- });
- });
- return str;
- };
-
- var style = {
- comment: 'color:#63a35c;',
- string: 'color:#183691;',
- number: 'color:#0086b3;',
- keyword: 'color:#a71d5d;',
- operator: 'color:#994500;'
- };
- var language = {};
- language.js = {
- comment: {
- re: /(\/\/.*|\/\*([\s\S]*?)\*\/)/g,
- style: 'comment'
- },
- string: {
- re: /(('.*?')|(".*?"))/g,
- style: 'string'
- },
- numbers: {
- re: /(-?(\d+|\d+\.\d+|\.\d+))/g,
- style: 'number'
- },
- keywords: {
- re: /(?:\b)(function|for|foreach|while|if|else|elseif|switch|break|as|return|this|class|self|default|var|const|let|false|true|null|undefined)(?:\b)/gi,
- style: 'keyword'
- },
- operator: {
- re: /(\+|-|\/|\*|%|=|<|>|\||\?|\.)/g,
- style: 'operator'
- }
- };
- language.html = {
- comment: {
- re: /(<!--([\s\S]*?)-->)/g,
- style: 'comment'
- },
- tag: {
- re: /(<\/?\w(.|\n)*?\/?>)/g,
- style: 'keyword',
- embed: ['string']
- },
- string: language.js.string,
- css: {
- re: /(?:<style.*?>)([\s\S]*)?(?:<\/style>)/gi,
- language: 'css'
- },
- script: {
- re: /(?:<script.*?>)([\s\S]*?)(?:<\/script>)/gi,
- language: 'js'
- }
- };
- language.css = {
- comment: language.js.comment,
- string: language.js.string,
- numbers: {
- re: /((-?(\d+|\d+\.\d+|\.\d+)(%|px|em|pt|in)?)|#[0-9a-fA-F]{3}[0-9a-fA-F]{3})/g,
- style: 'number'
- },
- keywords: {
- re: /(@\w+|:?:\w+|[a-z-]+:)/g,
- style: 'keyword'
- }
- };
-
- return exports;
-})({});
-
/* ------------------------------ values ------------------------------ */
export var values = _.values = (function (exports) {
@@ -2392,20 +2222,20 @@ export var isErr = _.isErr = (function (exports) {
/* ------------------------------ isErudaEl ------------------------------ */
export var isErudaEl = _.isErudaEl = (function (exports) {
- /* See if an element is within eruda.
- */
+ /* See if an element is within eruda.
+ */
- exports = function (el) {
- let parentNode = el.parentNode
+ exports = function(el) {
+ let parentNode = el.parentNode
- if (!parentNode) return false
+ if (!parentNode) return false
- while (parentNode) {
- parentNode = parentNode.parentNode
- if (parentNode && parentNode.id === 'eruda') return true
- }
+ while (parentNode) {
+ parentNode = parentNode.parentNode
+ if (parentNode && parentNode.id === 'eruda') return true
+ }
- return false
+ return false
}
return exports;
@@ -3093,10 +2923,10 @@ export var dateFormat = _.dateFormat = (function (exports) {
Z: gmt
? 'GMT'
: utc
- ? 'UTC'
- : (toStr(date).match(regTimezone) || [''])
- .pop()
- .replace(regTimezoneClip, ''),
+ ? 'UTC'
+ : (toStr(date).match(regTimezone) || [''])
+ .pop()
+ .replace(regTimezoneClip, ''),
o:
(o > 0 ? '-' : '+') +
padZero(Math.floor(Math.abs(o) / 60) * 100 + (Math.abs(o) % 60), 4),
@@ -3387,54 +3217,54 @@ export var difference = _.difference = (function (exports) {
/* ------------------------------ evalCss ------------------------------ */
export var evalCss = _.evalCss = (function (exports) {
- /* Eval css.
+ /* Eval css.
*/
/* dependencies
* toStr each filter
- */
+ */
- let styleList = []
- let scale = 1
+ let styleList = []
+ let scale = 1
- exports = function (css, container) {
- css = toStr(css)
+ exports = function(css, container) {
+ css = toStr(css)
- for (let i = 0, len = styleList.length; i < len; i++) {
- if (styleList[i].css === css) return
- }
+ for (let i = 0, len = styleList.length; i < len; i++) {
+ if (styleList[i].css === css) return
+ }
- container = container || exports.container || document.head
- const el = document.createElement('style')
+ container = container || exports.container || document.head
+ const el = document.createElement('style')
- el.type = 'text/css'
- container.appendChild(el)
+ el.type = 'text/css'
+ container.appendChild(el)
- let style = { css, el, container }
- resetStyle(style)
- styleList.push(style)
+ let style = { css, el, container }
+ resetStyle(style)
+ styleList.push(style)
- return style
- }
+ return style
+ }
- exports.setScale = function(s) {
- scale = s
- each(styleList, style => resetStyle(style))
- }
+ exports.setScale = function(s) {
+ scale = s
+ each(styleList, style => resetStyle(style))
+ }
- exports.clear = function() {
- each(styleList, ({ container, el }) => container.removeChild(el))
- styleList = []
- }
+ exports.clear = function() {
+ each(styleList, ({ container, el }) => container.removeChild(el))
+ styleList = []
+ }
- exports.remove = function(style) {
- styleList = filter(styleList, s => s !== style)
+ exports.remove = function(style) {
+ styleList = filter(styleList, s => s !== style)
- style.container.removeChild(style.el)
- }
+ style.container.removeChild(style.el)
+ }
- function resetStyle({ css, el }) {
- el.innerText = css.replace(/(\d+)px/g, ($0, $1) => +$1 * scale + 'px')
+ function resetStyle({ css, el }) {
+ el.innerText = css.replace(/(\d+)px/g, ($0, $1) => +$1 * scale + 'px')
}
return exports;
@@ -3615,6 +3445,181 @@ export var defaults = _.defaults = (function (exports) {
return exports;
})({});
+/* ------------------------------ highlight ------------------------------ */
+
+export var highlight = _.highlight = (function (exports) {
+ /* Highlight code.
+ *
+ * |Name |Type |Desc |
+ * |-------|------|----------------------------|
+ * |str |string|Code string |
+ * |lang=js|string|Language, js, html or css |
+ * |[style]|object|Keyword highlight style |
+ * |return |string|Highlighted html code string|
+ *
+ * Available styles:
+ *
+ * comment, string, number, keyword, operator
+ */
+
+ /* example
+ * highlight('const a = 5;', 'js', {
+ * keyword: 'color:#569cd6;'
+ * }); // -> 'const a = 5;'
+ */
+
+ /* typescript
+ * export declare namespace highlight {
+ * interface IStyle {
+ * comment?: string;
+ * string?: string;
+ * number?: string;
+ * keyword?: string;
+ * operator?: string;
+ * }
+ * }
+ * export declare function highlight(
+ * str: string,
+ * lang?: string,
+ * style?: highlight.IStyle
+ * ): string;
+ */
+
+ /* dependencies
+ * each defaults
+ */ // https://github.com/trentrichardson/jQuery-Litelighter
+
+ exports = function(str) {
+ var lang =
+ arguments.length > 1 && arguments[1] !== undefined
+ ? arguments[1]
+ : 'js';
+ var style =
+ arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ defaults(style, defStyle);
+ str = str.replace(//g, '>');
+ lang = language[lang];
+ var subLangSi = 0;
+ var subLangs = [];
+ each(lang, function(val) {
+ if (!val.language) return;
+ str = str.replace(val.re, function($1, $2) {
+ subLangs[subLangSi++] = exports($2, val.language);
+ return $1.replace($2, '___subtmpl' + (subLangSi - 1) + '___');
+ });
+ });
+ each(lang, function(val, key) {
+ if (language[val.language]) return;
+ str = str.replace(val.re, '___' + key + '___$1___end' + key + '___');
+ });
+ var levels = [];
+ str = str.replace(/___(?!subtmpl)\w+?___/g, function($0) {
+ var end = $0.substr(3, 3) === 'end',
+ tag = (!end ? $0.substr(3) : $0.substr(6)).replace(/_/g, ''),
+ lastTag = levels.length > 0 ? levels[levels.length - 1] : null;
+
+ if (
+ !end &&
+ (lastTag == null ||
+ tag == lastTag ||
+ (lastTag != null &&
+ lang[lastTag] &&
+ lang[lastTag].embed != undefined &&
+ lang[lastTag].embed.indexOf(tag) > -1))
+ ) {
+ levels.push(tag);
+ return $0;
+ } else if (end && tag == lastTag) {
+ levels.pop();
+ return $0;
+ }
+
+ return '';
+ });
+ each(lang, function(val, key) {
+ str = str
+ .replace(new RegExp('___end' + key + '___', 'g'), '')
+ .replace(
+ new RegExp('___' + key + '___', 'g'),
+ '
'
+ );
+ });
+ each(lang, function(val) {
+ if (!val.language) return;
+ str = str.replace(/___subtmpl\d+___/g, function($tmpl) {
+ var i = parseInt($tmpl.replace(/___subtmpl(\d+)___/, '$1'), 10);
+ return subLangs[i];
+ });
+ });
+ return str;
+ };
+
+ var defStyle = {
+ comment: 'color:#63a35c;',
+ string: 'color:#183691;',
+ number: 'color:#0086b3;',
+ keyword: 'color:#a71d5d;',
+ operator: 'color:#994500;'
+ };
+ var language = {};
+ language.js = {
+ comment: {
+ re: /(\/\/.*|\/\*([\s\S]*?)\*\/)/g,
+ style: 'comment'
+ },
+ string: {
+ re: /(('.*?')|(".*?"))/g,
+ style: 'string'
+ },
+ numbers: {
+ re: /(-?(\d+|\d+\.\d+|\.\d+))/g,
+ style: 'number'
+ },
+ keywords: {
+ re: /(?:\b)(function|for|foreach|while|if|else|elseif|switch|break|as|return|this|class|self|default|var|const|let|false|true|null|undefined)(?:\b)/gi,
+ style: 'keyword'
+ },
+ operator: {
+ re: /(\+|-|\/|\*|%|=|<|>|\||\?|\.)/g,
+ style: 'operator'
+ }
+ };
+ language.html = {
+ comment: {
+ re: /(<!--([\s\S]*?)-->)/g,
+ style: 'comment'
+ },
+ tag: {
+ re: /(<\/?\w(.|\n)*?\/?>)/g,
+ style: 'keyword',
+ embed: ['string']
+ },
+ string: language.js.string,
+ css: {
+ re: /(?:<style.*?>)([\s\S]*)?(?:<\/style>)/gi,
+ language: 'css'
+ },
+ script: {
+ re: /(?:<script.*?>)([\s\S]*?)(?:<\/script>)/gi,
+ language: 'js'
+ }
+ };
+ language.css = {
+ comment: language.js.comment,
+ string: language.js.string,
+ numbers: {
+ re: /((-?(\d+|\d+\.\d+|\.\d+)(%|px|em|pt|in)?)|#[0-9a-fA-F]{3}[0-9a-fA-F]{3})/g,
+ style: 'number'
+ },
+ keywords: {
+ re: /(@\w+|:?:\w+|[a-z-]+:)/g,
+ style: 'keyword'
+ }
+ };
+
+ return exports;
+})({});
+
/* ------------------------------ extend ------------------------------ */
export var extend = _.extend = (function (exports) {
@@ -4835,6 +4840,107 @@ export var $show = _.$show = (function (exports) {
return exports;
})({});
+/* ------------------------------ Stack ------------------------------ */
+
+export var Stack = _.Stack = (function (exports) {
+ /* Stack data structure.
+ *
+ * ### clear
+ *
+ * Clear the stack.
+ *
+ * ### push
+ *
+ * Add an item to the stack.
+ *
+ * |Name |Type |Desc |
+ * |------|------|------------|
+ * |item |* |Item to add |
+ * |return|number|Current size|
+ *
+ * ### pop
+ *
+ * Get the last item of the stack.
+ *
+ * ### peek
+ *
+ * Get the last item without removing it.
+ *
+ * ### forEach
+ *
+ * Iterate over the stack.
+ *
+ * |Name |Type |Desc |
+ * |--------|--------|--------------------------|
+ * |iterator|function|Function invoked iteration|
+ * |[ctx] |* |Function context |
+ *
+ * ### toArr
+ *
+ * Convert the stack to a JavaScript array.
+ */
+
+ /* example
+ * const stack = new Stack();
+ *
+ * stack.push(2); // -> 1
+ * stack.push(3); // -> 2
+ * stack.pop(); // -> 3
+ */
+
+ /* typescript
+ * export declare class Stack {
+ * size: number;
+ * clear(): void;
+ * push(item: any): number;
+ * pop(): any;
+ * peek(): any;
+ * forEach(iterator: Function, context?: any): void;
+ * toArr(): any[];
+ * }
+ */
+
+ /* dependencies
+ * Class
+ */
+
+ exports = Class({
+ initialize: function Stack() {
+ this.clear();
+ },
+ clear: function() {
+ this._items = [];
+ this.size = 0;
+ },
+ push: function(item) {
+ this._items.push(item);
+
+ return ++this.size;
+ },
+ pop: function() {
+ if (!this.size) return;
+ this.size--;
+ return this._items.pop();
+ },
+ peek: function() {
+ return this._items[this.size - 1];
+ },
+ forEach: function(iterator, ctx) {
+ ctx = arguments.length > 1 ? ctx : this;
+ var items = this._items;
+
+ for (var i = this.size - 1, j = 0; i >= 0; i--, j++) {
+ iterator.call(ctx, items[i], j, this);
+ }
+ },
+ toArr: function() {
+ return this._items.slice(0).reverse();
+ }
+ });
+
+ return exports;
+})({});
+
/* ------------------------------ delegate ------------------------------ */
export var delegate = _.delegate = (function (exports) {
@@ -7211,6 +7317,7 @@ export var Url = _.Url = (function (exports) {
}
if (slashes) {
+ var host = rest;
var hostEnd = -1;
for (var i = 0, len = hostEndingChars.length; i < len; i++) {
@@ -7219,8 +7326,11 @@ export var Url = _.Url = (function (exports) {
hostEnd = pos;
}
- var host = rest.slice(0, hostEnd);
- rest = rest.slice(hostEnd);
+ if (hostEnd > -1) {
+ host = rest.slice(0, hostEnd);
+ rest = rest.slice(hostEnd);
+ }
+
var atSign = host.lastIndexOf('@');
if (atSign !== -1) {
@@ -7478,7 +7588,7 @@ export var type = _.type = (function (exports) {
*/
/* typescript
- * export declare function type(val: any, lowerCase: boolean): string;
+ * export declare function type(val: any, lowerCase?: boolean): string;
*/
/* dependencies
diff --git a/test/util.js b/test/util.js
index 95d2bbe..f66777f 100644
--- a/test/util.js
+++ b/test/util.js
@@ -11,7 +11,6 @@
}(this, function ()
{
/* eslint-disable */
- "use strict";
var _ = {};
@@ -1269,54 +1268,54 @@
/* ------------------------------ evalCss ------------------------------ */
_.evalCss = (function (exports) {
- /* Eval css.
+ /* Eval css.
*/
/* dependencies
* toStr each filter
- */
+ */
- let styleList = []
- let scale = 1
+ let styleList = []
+ let scale = 1
- exports = function (css, container) {
- css = toStr(css)
+ exports = function(css, container) {
+ css = toStr(css)
- for (let i = 0, len = styleList.length; i < len; i++) {
- if (styleList[i].css === css) return
- }
+ for (let i = 0, len = styleList.length; i < len; i++) {
+ if (styleList[i].css === css) return
+ }
- container = container || exports.container || document.head
- const el = document.createElement('style')
+ container = container || exports.container || document.head
+ const el = document.createElement('style')
- el.type = 'text/css'
- container.appendChild(el)
+ el.type = 'text/css'
+ container.appendChild(el)
- let style = { css, el, container }
- resetStyle(style)
- styleList.push(style)
+ let style = { css, el, container }
+ resetStyle(style)
+ styleList.push(style)
- return style
- }
+ return style
+ }
- exports.setScale = function(s) {
- scale = s
- each(styleList, style => resetStyle(style))
- }
+ exports.setScale = function(s) {
+ scale = s
+ each(styleList, style => resetStyle(style))
+ }
- exports.clear = function() {
- each(styleList, ({ container, el }) => container.removeChild(el))
- styleList = []
- }
+ exports.clear = function() {
+ each(styleList, ({ container, el }) => container.removeChild(el))
+ styleList = []
+ }
- exports.remove = function(style) {
- styleList = filter(styleList, s => s !== style)
+ exports.remove = function(style) {
+ styleList = filter(styleList, s => s !== style)
- style.container.removeChild(style.el)
- }
+ style.container.removeChild(style.el)
+ }
- function resetStyle({ css, el }) {
- el.innerText = css.replace(/(\d+)px/g, ($0, $1) => +$1 * scale + 'px')
+ function resetStyle({ css, el }) {
+ el.innerText = css.replace(/(\d+)px/g, ($0, $1) => +$1 * scale + 'px')
}
return exports;