From 00acada6b59e75814c7390993987193140cec68d Mon Sep 17 00:00:00 2001 From: surunzi Date: Wed, 25 Sep 2019 21:11:16 +0800 Subject: [PATCH] feat: group support --- script/webpack.base.js | 9 +- src/Console/Console.js | 12 +- src/Console/Log.hbs | 7 +- src/Console/Log.js | 47 +++- src/Console/Logger.js | 52 +++- src/Console/Logger.scss | 48 +++- src/lib/handlebars.js | 66 +++++ src/lib/util.js | 548 ++++++++++++++++++++++++---------------- test/util.js | 65 +++-- 9 files changed, 567 insertions(+), 287 deletions(-) create mode 100644 src/lib/handlebars.js 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}} -
+
+
+
{{{msg}}}
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">(.*?)${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;