diff --git a/.gitignore b/.gitignore index 123ae94..5bf9284 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules + +#WebStorm +.idea diff --git a/app.ico b/app.ico new file mode 100644 index 0000000..ba16fe2 Binary files /dev/null and b/app.ico differ diff --git a/bin/app-configure.js b/bin/app-configure.js new file mode 100644 index 0000000..d7f2585 --- /dev/null +++ b/bin/app-configure.js @@ -0,0 +1,341 @@ +/** + * Created by nuintun on 2015/11/18. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var join = path.join; +// module to control application life +var app = require('app'); +var ipc = require('ipc-main'); +var dialog = require('dialog'); +var shell = require('shell'); + +const USERDATA = app.getPath('userData'); +const USERDESKTOP = app.getPath('userDesktop'); +const CONFIGURENAME = 'command-manager.config'; +const CONFIGUREPATH = join(USERDATA, CONFIGURENAME); +const DEFAULTCONFIGURE = { projects: [] }; +const ERRORMESSAGE = { + NONEXISTS: '不存在', + READERROR: '读取失败', + WRITEERROR: '写入失败', + PARSEERROR: '解析失败', + VALIDERROR: '校验失败' +}; + +/** + * ConfigureError + * @param code + * @param message + * @constructor + */ +function ConfigureError(code, message){ + this.code = code; + this.message = message; + this.name = 'ConfigureError'; +} + +// ConfigureError prototype +ConfigureError.prototype = Object.create(Error.prototype); +ConfigureError.prototype.constructor = ConfigureError; + +/** + * verify configure + * @param configure + * @returns {*} + */ +function verifyConfigure(configure){ + if (!configure) { + return false; + } + + if (!Array.isArray(configure.projects)) { + return false; + } + + return configure.projects.every(function (project){ + if (!project.name || typeof project.name !== 'string') { + return false; + } + + if (Array.isArray(project.env)) { + if ( + !project.env.every(function (env){ + return env.name && typeof env.name === 'string' + && env.value && typeof env.value === 'string'; + }) + ) { + return false; + } + } + + if (Array.isArray(project.command)) { + if ( + project.command.every(function (command){ + return command.name && typeof command.name === 'string' + && command.value && typeof command.value === 'string'; + }) + ) { + return false; + } + } + + return true; + }); +} + +/** + * unique array by a track + * @param array + * @param progress + * @param track + * @returns {Array} + */ +function unique(array, progress, track){ + var cache = {}; + + progress = typeof progress === 'function' ? progress : function (){}; + track = track && typeof track === 'string' ? track : 'name'; + + return array.filter(function (item){ + var key = item[track]; + + if (cache[key]) { + return false; + } else { + cache[key] = true; + + progress.apply(this, arguments); + + return true; + } + }); +} + +/** + * filter configure + * @param configure + * @returns {*} + */ +function filterConfigure(configure){ + configure.projects = unique(configure.projects, function (project){ + if (project.env) { + project.env = unique(project.env); + } + + if (project.command) { + project.command = unique(project.command); + } + }); + + return configure; +} + +/** + * AppConfigure + * @param window + * @param tray + * @constructor + */ +function AppConfigure(window, tray){ + this.window = window; + this.tray = tray; + this.title = window.getTitle(); + + this.init(); +} + +/** + * AppConfigure prototype + */ +AppConfigure.prototype = { + init: function (){ + var context = this; + + this.create(); + + ipc.on('app-configure', function (event, command){ + switch (command) { + case 'import': + context.import(function (configure){ + this.showMessageBox('配置文件导入成功!', { type: 'info' }); + event.sender.send('app-configure', 'refresh', configure); + }, function (error){ + this.showMessageBox('配置文件' + error.message + '!'); + }); + break; + case 'export': + context.export(function (path){ + this.showMessageBox('配置文件导出成功!', { type: 'info' }, function (){ + shell.showItemInFolder(path); + }); + }, function (){ + this.showMessageBox('配置文件导出失败!'); + }); + break; + case 'refresh': + context.read(function (configure){ + event.sender.send('app-configure', 'refresh', configure); + }, function (error){ + context.showMessageBox('配置文件' + error.message + '!', function (){ + context.window.close(); + }); + }); + break; + case 'add': + context.save(function (configure){ + + }, function (configure){ + + }); + break; + } + + }); + }, + create: function (){ + var context = this; + + fs.stat(CONFIGUREPATH, function (error, stats){ + if (error || !stats.isFile()) { + context.save(DEFAULTCONFIGURE, null, function (){ + context.showMessageBox('配置文件创建失败,请用管理员模式运行重试!', function (){ + context.window.close(); + }); + }); + } + }); + }, + save: function (configure, done, fail){ + var context = this; + + done = typeof done === 'function' ? done : function (){}; + fail = typeof fail === 'function' ? fail : function (){}; + + fs.writeFile(CONFIGUREPATH, JSON.stringify(configure), function (error){ + if (error) { + var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'WRITEERROR'; + + fail.call(context, new ConfigureError(code, ERRORMESSAGE[code])); + } else { + done.call(context, configure); + } + }); + }, + read: function (done, fail){ + var context = this; + + done = typeof done === 'function' ? done : function (){}; + fail = typeof fail === 'function' ? fail : function (){}; + + fs.readFile(CONFIGUREPATH, function (error, configure){ + if (error) { + var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR'; + + fail.call(context, new ConfigureError(code, ERRORMESSAGE[code])); + } else { + try { + configure = JSON.parse(configure); + } catch (error) { + return fail.call(context, new ConfigureError('PARSEERROR', ERRORMESSAGE.PARSEERROR)); + } + } + + done.call(context, configure); + }); + }, + import: function (done, fail){ + var context = this; + + done = typeof done === 'function' ? done : function (){}; + fail = typeof fail === 'function' ? fail : function (){}; + + // show open dialog + dialog.showOpenDialog(this.window, { + title: this.title, + defaultPath: CONFIGURENAME, + properties: ['openFile'], + filters: [{ name: 'Config Files', extensions: ['config'] }] + }, function (paths){ + if (paths) { + fs.readFile(paths[0], function (error, configure){ + if (error) { + var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR'; + + fail.call(context, new ConfigureError(code, ERRORMESSAGE[code])); + } else { + try { + configure = JSON.parse(configure); + } catch (error) { + return fail.call(context, new ConfigureError('PARSEERROR', ERRORMESSAGE.PARSEERROR)); + } + + // verify configure + var invalid = !verifyConfigure(configure); + + // invalid configure + if (invalid) { + return fail.call(context, new ConfigureError('VALIDERROR', ERRORMESSAGE.VALIDERROR)); + } + + // filter configure + configure = filterConfigure(configure); + + // save configure + context.save(configure, done, fail); + } + }); + } + }); + }, + export: function (done, fail){ + var context = this; + + done = typeof done === 'function' ? done : function (){}; + fail = typeof fail === 'function' ? fail : function (){}; + + // show save dialog + dialog.showSaveDialog(this.window, { + title: this.title, + defaultPath: join(USERDESKTOP, CONFIGURENAME), + filters: [{ name: 'Config Files', extensions: ['config'] }] + }, function (path){ + if (path) { + fs.createReadStream(CONFIGUREPATH) + .on('error', function (error){ + var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR'; + + fail.call(context, new ConfigureError(code, ERRORMESSAGE[code])); + }) + .pipe(fs.createWriteStream(path)) + .on('finish', function (){ + done.call(context, path); + }) + .on('error', function (error){ + var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'WRITEERROR'; + + fail.call(context, new ConfigureError(code, ERRORMESSAGE[code])); + }); + } + }); + }, + showMessageBox: function (message, options, callback){ + if (typeof options === 'function') { + callback = options; + options = { message: message }; + } + + options = options || {}; + options.title = this.title; + options.message = message; + options.type = options.type || 'error'; + options.buttons = options.buttons || []; + + dialog.showMessageBox(this.window, options, callback); + } +}; + +module.exports = AppConfigure; diff --git a/bin/open-directory.js b/bin/open-directory.js new file mode 100644 index 0000000..327cf97 --- /dev/null +++ b/bin/open-directory.js @@ -0,0 +1,28 @@ +/** + * Created by nuintun on 2015/11/18. + */ + +'use strict'; + +// module to control application life +var ipc = require('ipc-main'); +var dialog = require('dialog'); + +/** + * open directory + * @param window + */ +module.exports = function (window){ + // listen open directory ipc + ipc.on('open-directory', function (event, path, uid){ + dialog.showOpenDialog(window, { + title: window.getTitle(), + properties: ['openDirectory'], + defaultPath: path || '' + }, function (directorys){ + if (directorys) { + event.sender.send('select-directory', directorys, uid); + } + }); + }); +}; diff --git a/bin/window-control.js b/bin/window-control.js new file mode 100644 index 0000000..cea6f61 --- /dev/null +++ b/bin/window-control.js @@ -0,0 +1,59 @@ +/** + * Created by nuintun on 2015/11/18. + */ + +'use strict'; + +// module to control application life +var ipc = require('ipc-main'); + +/** + * window control + * @param icon + * @param window + * @param tray + */ +module.exports = function (icon, window, tray){ + // bind maximize event + window.on('maximize', function (event){ + event.sender.send('is-maximized', true); + }); + + // bind unmaximize event + window.on('unmaximize', function (event){ + event.sender.send('is-maximized', false); + }); + + // bind tray double-click event + tray.on('double-click', function (){ + window.show(); + }); + + // listen window ipc + ipc.on('window', function (event, command){ + switch (command) { + case 'tray': + var title = window.getTitle(); + + window.hide(); + tray.displayBalloon({ + icon: icon, + title: title, + content: title + '正在后台运行!' + }); + break; + case 'close': + window.close(); + break; + case 'maximize': + window.maximize(); + break; + case 'unmaximize': + window.unmaximize(); + break; + case 'is-maximized': + event.sender.send('is-maximized', window.isMaximized()); + break; + } + }); +}; diff --git a/css/base.css b/css/base.css new file mode 100644 index 0000000..d748393 --- /dev/null +++ b/css/base.css @@ -0,0 +1,136 @@ +@charset "utf-8"; + +/* alice.base 样式模块 */ + +/* 防止用户自定义背景颜色对网页的影响,添加让用户可以自定义字体 */ +html { + color: #000; background: #fff; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +/* 内外边距通常让各个浏览器样式的表现位置不同 */ +body, div, dl, dt, dd, ul, +ol, li, h1, h2, h3, h4, h5, +h6, pre, code, form, fieldset, +legend, input, textarea, p, blockquote, +th, td, hr, button, article, aside, details, +figcaption, figure, footer, header, hgroup, menu, nav, section { + margin: 0; padding: 0; +} +/* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */ +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { + display: block; +} +/* HTML5 媒体文件跟 img 保持一致 */ +audio, canvas, video { + display: inline-block; *display: inline; *zoom: 1; +} +/* 要注意表单元素并不继承父级 font 的问题 */ +body, button, input, select, textarea { + font: 12px/1.5 Microsoft Yahei, tahoma, arial, "Hiragino Sans GB", \5b8b\4f53; +} +input, select, textarea { + font-size: 100%; +} +/* 去掉各Table cell 的边距并让其边重合 */ +table { + border-collapse: collapse; border-spacing: 0; +} +/* IE bug fixed: th 不继承 text-align*/ +th { + text-align: inherit; +} +/* 去除默认边框 */ +fieldset, img { + border: 0; +} +/* ie6 7 8(q) bug 显示为行内表现 */ +iframe { + display: block; +} +/* 去掉 firefox 下此元素的边框 */ +abbr, acronym { + border: 0; font-variant: normal; +} +/* 一致的 del 样式 */ +del { + text-decoration: line-through; +} +address, caption, cite, code, dfn, em, th, var { + font-style: normal; + font-weight: 500; +} +/* 去掉列表前的标识, li 会继承 */ +ol, ul { + list-style: none; +} +/* 对齐是排版最重要的因素, 别让什么都居中 */ +caption, th { + text-align: left; +} +/* 来自yahoo, 让标题都自定义, 适应多个系统应用 */ +h1, h2, h3, h4, h5, h6 { + font-size: 100%; + font-weight: 500; +} +q:before, q:after { + content: ''; +} +/* 统一上标和下标 */ +sub, sup { + font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; +} +sup { top: -0.5em; } +sub { bottom: -0.25em; } +/* 正常链接 未访问 */ +a:link { + color: #08c; +} +/* 鼠标悬停 */ +a:hover { + color: #08c; + text-decoration: underline; +} +/* 默认不显示下划线,保持页面简洁 */ +ins, a { + text-decoration: none; +} +/* 代码字体 */ +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} +/* 清理浮动 */ +.fn-clear:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} +.fn-clear { + zoom: 1; /* for IE6 IE7 */ +} +/* 隐藏, 通常用来与 JS 配合 */ +body .fn-hide { + display: none; +} +/* visibility 隐藏, 通常用来与 JS 配合 */ +body .fn-invisible { + visibility: hidden; +} +/* 设置内联, 减少浮动带来的bug */ +.fn-left, +.fn-right { + display: inline; +} +.fn-left { + float: left; +} +.fn-right { + float: right; +} diff --git a/css/index.css b/css/index.css new file mode 100644 index 0000000..6ef5af0 --- /dev/null +++ b/css/index.css @@ -0,0 +1,456 @@ +@font-face { + font-family: font-icon; + src: url('../fonts/font-icon.svg#font-icon') format('svg'), + url("../fonts/font-icon.woff") format("woff"), + url("../fonts/font-icon.ttf") format("truetype"); + font-weight: normal; + font-style: normal; + font-variant: normal; +} +[class^="icon-"], +[class*=" icon-"] { + speak: none; + outline: none; + line-height: 1; + font-size: 20px; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + vertical-align: middle; + display: inline-block; + font-family: font-icon, sans-serif; + /* enable ligatures */ + -webkit-font-feature-settings: "liga"; + font-feature-settings: "liga"; + /* better font rendering */ + -webkit-font-smoothing: antialiased; + /* min font size */ + -webkit-text-size-adjust: none; +} +.icon-play:before { + content: "\e600"; + font-size: 16px; + color: #2f9c23; + vertical-align: 3px; +} +.icon-stop:before { + content: "\e601"; + font-size: 15px; + color: #f00; + vertical-align: 3px; +} +.icon-folder:before { + content: "\e602"; + font-size: 16px; + margin-right: 3px; + vertical-align: 5px; +} +.icon-maximize:before { + content: "\e603"; + font-size: 17px; + vertical-align: 2px; +} +.icon-unmaximize:before { + content: "\e604"; + font-size: 17px; + vertical-align: 2px; +} +.icon-plus:before { + content: "\e605"; + font-size: 17px; + vertical-align: 2px; +} +.icon-cross:before { + content: "\e606"; + font-size: 24px; +} +.icon-import:before { + content: "\e607"; + font-size: 14px; + vertical-align: 3px; +} +.icon-export:before { + content: "\e608"; + font-size: 14px; + vertical-align: 3px; +} +.icon-minimize-tray:before { + content: "\e609"; + font-size: 24px; +} +.icon-expand:before { + content: "\e610"; + font-size: 20px; +} +.icon-ellipsis:before { + content: "\e611"; + font-size: 20px; +} +.icon-trash:before { + content: "\e612"; + font-size: 16px; + color: #f00; + vertical-align: 2px; +} +.icon-gear:before { + content: "\e613"; + font-size: 18px; + vertical-align: 1px; +} +.icon-ellipsis { + width: 40px; + height: 26px; + line-height: 26px; + text-align: center; + color: #fff !important; + background-color: #f57527; + transition: box-shadow .25s ease-in-out; +} +.icon-ellipsis:hover { + box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 3px rgb(0, 120, 255); +} +::-webkit-scrollbar-track-piece { + background-color: #fff; + border-radius: 0; +} +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-thumb { + height: 50px; + background-color: #999; + border-radius: 4px; + outline: 2px solid #fff; + outline-offset: -2px; + border: 2px solid #fff; +} +::-webkit-scrollbar-thumb:hover { + height: 50px; + background-color: #9f9f9f; + border-radius: 4px; +} +input[type=text] { + resize: none; + height: 24px; + line-height: 24px; + outline: none; + padding: 0 5px; + font-size: 13px; + border: 1px solid #ccc; + background-color: #ffffcd; + box-shadow: 2px 2px 3px #ededed inset; + transition: border .25s ease-in-out, box-shadow .25s ease-in-out; +} +input[type=text]:focus { + border: 1px solid rgba(82, 162, 235, .8); + box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 6px rgba(82, 162, 235, 1); +} +html, +body { + width: 100%; height: 100%; overflow: hidden; +} +body { + border: 1px solid #ccc; + box-sizing: border-box; + min-width: 1024px; !important; + min-height: 768px !important; +} +a { + outline: none; +} +a:hover { + text-decoration: none; +} +.ui-button { + color: #fff; + border: none; + outline: none; + background-color: #0e7fd4; + height: 26px; + line-height: 26px; + padding: 0 13px; + font-size: 13px; + overflow: auto; + display: inline-block; + vertical-align: middle; + cursor: pointer; + transition: box-shadow .25s ease-in-out; +} +.ui-button:hover { + box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 3px rgb(0, 120, 255); +} +.ui-button-orange { + background-color: #f57527; +} +header { + padding: 3px 6px; + position: relative; + -webkit-app-region: drag; + border-bottom: 1px solid #ccc; +} +header [class^="icon-"], +header [class*=" icon-"] { + -webkit-app-region: no-drag; +} +.icon-trash, +.ui-project-add, +.ui-import-configure, +.ui-export-configure { + transition: transform .09s linear; +} +.icon-gear { + transition: transform .15s linear; +} +.ui-window-control a { + display: inline-block; color: #9c9c9c; + transition: transform .25s linear; +} +.icon-gear:hover, +.ui-window-control a:hover { + transform: rotate(360deg); +} +.icon-trash:hover, +.ui-project-add:hover, +.ui-import-configure:hover, +.ui-export-configure:hover { + transform: scale(1.2); +} +.ui-project-add, +.ui-import-configure, +.ui-export-configure { + font-size: 0; + line-height: 0; + display: inline-block; + vertical-align: middle; +} +.ui-project-add .icon-expand { + margin-left: -5px; +} +.ui-import-configure { + margin: 0 1px 0 6px; +} +.ui-popup { + position: absolute; + top: 31px; + left: 5px; + border: 1px solid #ccc; + z-index: 1; + background-color: #fff; + box-shadow: 0 0 5px 0 #a9a9a9; +} +.ui-popup input, +.ui-project-configure input { + vertical-align: middle; +} +.ui-popup-arrow { + position: absolute; + top: -8px; left: 9px; + z-index: 1; +} +.ui-popup-arrow em, +.ui-popup-arrow span { + width: 0; height: 0; + border: solid rgba(255, 255, 255, 0); + position: absolute; + overflow: hidden; +} +.ui-popup-arrow em { + border-width: 0 7px 7px; + border-bottom-color: #ccc; +} +.ui-popup-arrow span { + top: 1px; + border-width: 0 7px 7px; + border-bottom-color: #fff; +} +.ui-popup-content { + padding: 20px; +} +.ui-popup-content li, +.ui-project-configure li { + color: #333; + margin: 0 0 16px 0; +} +.ui-popup-content strong, +.ui-project-configure strong { + font-size: 13px; + display: inline-block; + vertical-align: top; +} +.ui-popup-content input[type=text], +.ui-project-configure input[type=text] { + width: 180px; +} +.ui-popup-content .ui-popup-control { + margin: 0; + text-align: center; +} +.ui-project-tree { + box-sizing: border-box; + width: 20%; + min-height: 735px !important; + height: calc(100% - 31px); + overflow: auto; +} +.ui-project-tree li:nth-child(odd) { + background-color: #eee; +} +.ui-project-tree li { + color: #333; + width: 100%; + overflow: hidden; + cursor: pointer; +} +.ui-project-tree li a { + width: 100%; + height: 28px; + line-height: 28px; + padding: 0 0 0 10px; + box-sizing: border-box; + display: inline-block; + vertical-align: middle; +} +.ui-project-main { + box-sizing: border-box; + width: 80%; + height: calc(100% - 31px); + min-height: 735px !important; + overflow: hidden; +} +.ui-project-stage, +.ui-project-setting, +.ui-project-setting form { + width: 100%; + height: 100%; +} +.ui-control-bar { + height: 39px; + line-height: 39px; + border-left: 1px solid #ccc; +} +.ui-control-bar a { + display: inline-block; +} +.ui-control-operate a { + height: 39px; + line-height: 39px; +} +.ui-control-bar .icon-trash { + margin: 0 0 0 10px; +} +.ui-command { + position: relative; +} +.ui-command li { + float: left; + font-size: 14px; + color: #333; +} +.ui-command a { + padding: 0 6px; +} +.ui-command-more .icon-expand { + font-size: 24px; + color: #08c; + margin: -4px 0 0 -4px; +} +.ui-command-popup { + position: absolute; + top: 39px; right: 0; + background-color: #eee; +} +.ui-command-popup li { + float: none; + line-height: 30px; +} +.ui-command-popup li a { + padding: 0 10px; +} +.ui-project-tree a, +.ui-command li a, +.ui-command-popup li a { + color: inherit; +} +.ui-project-tree a:hover, +.ui-project-tree .active, +.ui-command a:hover { + background-color: #0e7fd4; + color: #fff; +} +.ui-command a:hover .icon-play:before { + color: #34bd24; +} +.ui-project-tree a:hover .icon-folder:before, +.ui-command a:hover .icon-expand:before { + color: #fff; +} +.ui-project-terminal, +.ui-project-configure { + width: 100%; + box-sizing: border-box; + height: calc(100% - 39px); + border-top: 1px dashed #ccc; + border-left: 1px solid #ccc; +} +.ui-project-terminal { + background-color: #181818; + border-left: 1px dashed #ccc; +} +.ui-control-bar .ui-button-orange { + margin: 0 10px 0 0; +} +.ui-project-configure { + overflow: auto; + text-align: center; +} +.ui-project-configure ul { + padding: 30px 0; + text-align: left; + display: inline-block; +} +.ui-sub-item > label, +.ui-sub-item ul { + vertical-align: top; +} +.ui-sub-item > label { + vertical-align: -3px; +} +.ui-sub-item ul { + padding: 0; +} +.ui-sub-item .ui-action-bar { + margin: 0; +} +.ui-sub-item span { + width: 192px; + line-height: 25px; + border-bottom: 1px dotted #ccc; + display: inline-block; + vertical-align: middle; +} +.ui-sub-item .icon-trash { + cursor: pointer; + margin-left: 6px; + vertical-align: middle; +} +.ui-project-tree a, +.ui-sub-item span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.ui-sub-item .ui-item-error { + margin: 10px 0 0; +} +.ui-item-error span { + color: #f00; + border-bottom-color: #f00; +} +.ui-item-error .icon-expand { + position: absolute; + transform: rotate(180deg); + margin-top: -11px; + margin-left: -5px; +} diff --git a/fonts/font-icon.svg b/fonts/font-icon.svg new file mode 100644 index 0000000..e034b7b --- /dev/null +++ b/fonts/font-icon.svg @@ -0,0 +1,24 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/font-icon.ttf b/fonts/font-icon.ttf new file mode 100644 index 0000000..85c2f1d Binary files /dev/null and b/fonts/font-icon.ttf differ diff --git a/fonts/font-icon.woff b/fonts/font-icon.woff new file mode 100644 index 0000000..a437ed5 Binary files /dev/null and b/fonts/font-icon.woff differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..0a9dc58 --- /dev/null +++ b/index.html @@ -0,0 +1,74 @@ + + + + 命令管理器 + + + + + + +
+ + +
+ +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+ + +
+
+
+
    +
  • + + +
  • +
  • + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
+
+
+ + \ No newline at end of file diff --git a/js/app/index.js b/js/app/index.js new file mode 100644 index 0000000..09a081d --- /dev/null +++ b/js/app/index.js @@ -0,0 +1,35 @@ +/** + * Created by nuintun on 2015/11/16. + */ + +'use strict'; + +var ipc = require('ipc-renderer'); +var Vue = require('./js/vue/vue'); + +require('./js/components/app-configure'); +require('./js/components/window-control'); +require('./js/components/app-nav'); +require('./js/components/directory'); +require('./js/components/dynamic-item'); + +window.addEventListener('DOMContentLoaded', function (){ + var app = new Vue({ + el: '#app', + data: { + activeIndex: 0, + configure: {} + } + }); + + ipc.on('app-configure', function (event, command, configure){ + switch (command) { + case 'refresh': + app.activeIndex = 0; + app.configure = configure; + break; + } + }); + + ipc.send('app-configure', 'refresh'); +}, false); diff --git a/js/components/app-configure/app-configure.html b/js/components/app-configure/app-configure.html new file mode 100644 index 0000000..df78f94 --- /dev/null +++ b/js/components/app-configure/app-configure.html @@ -0,0 +1,35 @@ +
+ + + + + + + + + + +
+
+
+ + +
+
+
+
    +
  • + + +
  • + +
  • + + +
  • +
+
+
+
\ No newline at end of file diff --git a/js/components/app-configure/index.js b/js/components/app-configure/index.js new file mode 100644 index 0000000..e7a8584 --- /dev/null +++ b/js/components/app-configure/index.js @@ -0,0 +1,26 @@ +/** + * Created by nuintun on 2015/11/19. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var ipc = require('ipc-renderer'); +var Vue = require('../../vue/vue'); + +module.exports = Vue.component('app-configure', { + template: fs.readFileSync(path.join(__dirname, 'app-configure.html')).toString(), + props: { + configure: { + type: Object, + twoWay: true, + required: true + } + }, + methods: { + appConfigure: function (command, configure){ + ipc.send('app-configure', command, configure); + } + } +}); diff --git a/js/components/app-main/app-main.html b/js/components/app-main/app-main.html new file mode 100644 index 0000000..e69de29 diff --git a/js/components/app-main/index.js b/js/components/app-main/index.js new file mode 100644 index 0000000..16550f9 --- /dev/null +++ b/js/components/app-main/index.js @@ -0,0 +1,3 @@ +/** + * Created by nuintun on 2015/11/20. + */ diff --git a/js/components/app-nav/app-nav.html b/js/components/app-nav/app-nav.html new file mode 100644 index 0000000..051fd40 --- /dev/null +++ b/js/components/app-nav/app-nav.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/js/components/app-nav/index.js b/js/components/app-nav/index.js new file mode 100644 index 0000000..6796ac3 --- /dev/null +++ b/js/components/app-nav/index.js @@ -0,0 +1,30 @@ +/** + * Created by nuintun on 2015/11/20. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var Vue = require('../../vue/vue'); + +module.exports = Vue.component('app-nav', { + template: fs.readFileSync(path.join(__dirname, 'app-nav.html')).toString(), + props: { + activeIndex: { + type: Number, + twoWay: true, + required: true + }, + configure: { + type: Object, + twoWay: true, + required: true + } + }, + methods: { + select: function (index){ + this.activeIndex = index; + } + } +}); diff --git a/js/components/directory/directory.html b/js/components/directory/directory.html new file mode 100644 index 0000000..a01466a --- /dev/null +++ b/js/components/directory/directory.html @@ -0,0 +1,3 @@ + +  + \ No newline at end of file diff --git a/js/components/directory/index.js b/js/components/directory/index.js new file mode 100644 index 0000000..aacae61 --- /dev/null +++ b/js/components/directory/index.js @@ -0,0 +1,39 @@ +/** + * Created by nuintun on 2015/11/17. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var ipc = require('ipc-renderer'); +var Vue = require('../../vue/vue'); + +module.exports = Vue.component('directory', { + // camelCase in JavaScript + props: { + label: { + type: String, + required: true + }, + path: { + type: String, + default: '' + } + }, + template: fs.readFileSync(path.join(__dirname, 'directory.html')).toString(), + methods: { + open: function (){ + ipc.send('open-directory', this.path, this._uid); + } + }, + created: function (){ + var context = this; + + ipc.on('select-directory', function (event, paths, uid){ + if (context._uid === uid) { + context.path = paths[0]; + } + }); + } +}); diff --git a/js/components/dynamic-item/dynamic-item.html b/js/components/dynamic-item/dynamic-item.html new file mode 100644 index 0000000..b820726 --- /dev/null +++ b/js/components/dynamic-item/dynamic-item.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/js/components/dynamic-item/index.js b/js/components/dynamic-item/index.js new file mode 100644 index 0000000..83d078e --- /dev/null +++ b/js/components/dynamic-item/index.js @@ -0,0 +1,90 @@ +/** + * Created by nuintun on 2015/11/17. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var Vue = require('../../vue/vue'); + +module.exports = Vue.component('dynamic-item', { + // camelCase in JavaScript + props: { + nameLabel: { + type: String, + required: true, + default: '' + }, + valueLabel: { + type: String, + required: true, + default: '' + }, + items: { + type: Array, + default: function (){ + return []; + } + } + }, + template: fs.readFileSync(path.join(__dirname, 'dynamic-item.html')).toString(), + data: function (){ + return { + name: '', + value: '', + _cached: {}, + nameError: '', + valueError: '' + }; + }, + methods: { + add: function (){ + // trim value + this.name = this.name.trim(); + this.value = this.value.trim(); + + // name error + if (!this.name) { + this.nameError = '不能为空'; + } else if (this.$data._cached[this.name]) { + this.nameError = ' ' + this.name + ' 已存在'; + } else { + this.nameError = ''; + } + + // value error + if (!this.value) { + this.valueError = '不能为空'; + } else { + this.valueError = ''; + } + + // add item + if (this.name && this.value && !this.$data._cached[this.name]) { + // cache name + this.$data._cached[this.name] = true; + // add item + this.items.push({ name: this.name, value: this.value }); + + // clean input + this.name = ''; + this.value = ''; + } + }, + focus: function (key){ + this[key] = ''; + }, + remove: function (index){ + var item = this.items[index]; + + this.items.splice(index, 1); + delete this.$data._cached[item.name]; + } + }, + created: function (){ + this.items.forEach(function (item){ + this.$data._cached[item.name] = true; + }, this); + } +}); diff --git a/js/components/project-configure/index.js b/js/components/project-configure/index.js new file mode 100644 index 0000000..16550f9 --- /dev/null +++ b/js/components/project-configure/index.js @@ -0,0 +1,3 @@ +/** + * Created by nuintun on 2015/11/20. + */ diff --git a/js/components/project-configure/project-configure.html b/js/components/project-configure/project-configure.html new file mode 100644 index 0000000..e69de29 diff --git a/js/components/window-control/index.js b/js/components/window-control/index.js new file mode 100644 index 0000000..6de17a8 --- /dev/null +++ b/js/components/window-control/index.js @@ -0,0 +1,39 @@ +/** + * Created by nuintun on 2015/11/19. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var ipc = require('ipc-renderer'); +var Vue = require('../../vue/vue'); + +module.exports = Vue.component('window-control', { + data: function (){ + return { + isMaximized: false + } + }, + template: fs.readFileSync(path.join(__dirname, 'window-control.html')).toString(), + methods: { + tray: function (){ + ipc.send('window', 'tray'); + }, + close: function (){ + ipc.send('window', 'close'); + }, + maximize: function (){ + ipc.send('window', this.isMaximized ? 'unmaximize' : 'maximize'); + } + }, + created: function (){ + var context = this; + + ipc.on('is-maximized', function (event, maximized){ + context.isMaximized = maximized; + }); + + ipc.send('window', 'is-maximized'); + } +}); diff --git a/js/components/window-control/window-control.html b/js/components/window-control/window-control.html new file mode 100644 index 0000000..20a73af --- /dev/null +++ b/js/components/window-control/window-control.html @@ -0,0 +1,11 @@ +
+ + + + + + + + + +
\ No newline at end of file diff --git a/js/vue/vue.js b/js/vue/vue.js new file mode 100644 index 0000000..3bc6abf --- /dev/null +++ b/js/vue/vue.js @@ -0,0 +1,10538 @@ +/*! + * Vue.js v1.0.8 + * (c) 2015 Evan You + * Released under the MIT License. + */ +(function webpackUniversalModuleDefinition(root, factory){ + if (typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if (typeof define === 'function' && define.amd) + define([], factory); + else if (typeof exports === 'object') + exports["Vue"] = factory(); + else + root["Vue"] = factory(); +})(this, function (){ + return /******/ (function (modules){ // webpackBootstrap + /******/ // The module cache + /******/ + var installedModules = {}; + + /******/ // The require function + /******/ + function __webpack_require__(moduleId){ + + /******/ // Check if module is in cache + /******/ + if (installedModules[moduleId]) + /******/ return installedModules[moduleId].exports; + + /******/ // Create a new module (and put it into the cache) + /******/ + var module = installedModules[moduleId] = { + /******/ exports: {}, + /******/ id: moduleId, + /******/ loaded: false + /******/ + }; + + /******/ // Execute the module function + /******/ + modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + + /******/ // Flag the module as loaded + /******/ + module.loaded = true; + + /******/ // Return the exports of the module + /******/ + return module.exports; + /******/ + } + + /******/ // expose the modules object (__webpack_modules__) + /******/ + __webpack_require__.m = modules; + + /******/ // expose the module cache + /******/ + __webpack_require__.c = installedModules; + + /******/ // __webpack_public_path__ + /******/ + __webpack_require__.p = ""; + + /******/ // Load entry module and return exports + /******/ + return __webpack_require__(0); + /******/ + }) + /************************************************************************/ + /******/([ + /* 0 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var extend = _.extend + + /** + * The exposed Vue constructor. + * + * API conventions: + * - public API methods/properties are prefiexed with `$` + * - internal methods/properties are prefixed with `_` + * - non-prefixed properties are assumed to be proxied user + * data. + * + * @constructor + * @param {Object} [options] + * @public + */ + + function Vue(options){ + this._init(options) + } + + /** + * Mixin global API + */ + + extend(Vue, __webpack_require__(13)) + + /** + * Vue and every constructor that extends Vue has an + * associated options object, which can be accessed during + * compilation steps as `this.constructor.options`. + * + * These can be seen as the default options of every + * Vue instance. + */ + + Vue.options = { + replace: true, + directives: __webpack_require__(16), + elementDirectives: __webpack_require__(50), + filters: __webpack_require__(53), + transitions: {}, + components: {}, + partials: {} + } + + /** + * Build up the prototype + */ + + var p = Vue.prototype + + /** + * $data has a setter which does a bunch of + * teardown/setup work + */ + + Object.defineProperty(p, '$data', { + get: function (){ + return this._data + }, + set: function (newData){ + if (newData !== this._data) { + this._setData(newData) + } + } + }) + + /** + * Mixin internal instance methods + */ + + extend(p, __webpack_require__(55)) + extend(p, __webpack_require__(56)) + extend(p, __webpack_require__(57)) + extend(p, __webpack_require__(60)) + extend(p, __webpack_require__(62)) + + /** + * Mixin public API methods + */ + + extend(p, __webpack_require__(63)) + extend(p, __webpack_require__(64)) + extend(p, __webpack_require__(65)) + extend(p, __webpack_require__(66)) + + Vue.version = '1.0.8' + module.exports = _.Vue = Vue + + /* istanbul ignore if */ + if (true) { + if (_.inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { + window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('init', Vue) + } + } + + /***/ + }, + /* 1 */ + /***/ function (module, exports, __webpack_require__){ + + var lang = __webpack_require__(2) + var extend = lang.extend + + extend(exports, lang) + extend(exports, __webpack_require__(3)) + extend(exports, __webpack_require__(4)) + extend(exports, __webpack_require__(10)) + extend(exports, __webpack_require__(11)) + extend(exports, __webpack_require__(12)) + + /***/ + }, + /* 2 */ + /***/ function (module, exports){ + + /** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + * + * @param {Object} obj + * @param {String} key + * @param {*} val + * @public + */ + + exports.set = function set(obj, key, val){ + if (obj.hasOwnProperty(key)) { + obj[key] = val + return + } + if (obj._isVue) { + set(obj._data, key, val) + return + } + var ob = obj.__ob__ + if (!ob) { + obj[key] = val + return + } + ob.convert(key, val) + ob.dep.notify() + if (ob.vms) { + var i = ob.vms.length + while (i--) { + var vm = ob.vms[i] + vm._proxy(key) + vm._digest() + } + } + } + + /** + * Delete a property and trigger change if necessary. + * + * @param {Object} obj + * @param {String} key + */ + + exports.delete = function (obj, key){ + if (!obj.hasOwnProperty(key)) { + return + } + delete obj[key] + var ob = obj.__ob__ + if (!ob) { + return + } + ob.dep.notify() + if (ob.vms) { + var i = ob.vms.length + while (i--) { + var vm = ob.vms[i] + vm._unproxy(key) + vm._digest() + } + } + } + + /** + * Check if an expression is a literal value. + * + * @param {String} exp + * @return {Boolean} + */ + + var literalValueRE = /^\s?(true|false|[\d\.]+|'[^']*'|"[^"]*")\s?$/ + exports.isLiteral = function (exp){ + return literalValueRE.test(exp) + } + + /** + * Check if a string starts with $ or _ + * + * @param {String} str + * @return {Boolean} + */ + + exports.isReserved = function (str){ + var c = (str + '').charCodeAt(0) + return c === 0x24 || c === 0x5F + } + + /** + * Guard text output, make sure undefined outputs + * empty string + * + * @param {*} value + * @return {String} + */ + + exports.toString = function (value){ + return value == null + ? '' + : value.toString() + } + + /** + * Check and convert possible numeric strings to numbers + * before setting back to data + * + * @param {*} value + * @return {*|Number} + */ + + exports.toNumber = function (value){ + if (typeof value !== 'string') { + return value + } else { + var parsed = Number(value) + return isNaN(parsed) + ? value + : parsed + } + } + + /** + * Convert string boolean literals into real booleans. + * + * @param {*} value + * @return {*|Boolean} + */ + + exports.toBoolean = function (value){ + return value === 'true' + ? true + : value === 'false' + ? false + : value + } + + /** + * Strip quotes from a string + * + * @param {String} str + * @return {String | false} + */ + + exports.stripQuotes = function (str){ + var a = str.charCodeAt(0) + var b = str.charCodeAt(str.length - 1) + return a === b && (a === 0x22 || a === 0x27) + ? str.slice(1, -1) + : str + } + + /** + * Camelize a hyphen-delmited string. + * + * @param {String} str + * @return {String} + */ + + var camelizeRE = /-(\w)/g + exports.camelize = function (str){ + return str.replace(camelizeRE, toUpper) + } + + function toUpper(_, c){ + return c ? c.toUpperCase() : '' + } + + /** + * Hyphenate a camelCase string. + * + * @param {String} str + * @return {String} + */ + + var hyphenateRE = /([a-z\d])([A-Z])/g + exports.hyphenate = function (str){ + return str + .replace(hyphenateRE, '$1-$2') + .toLowerCase() + } + + /** + * Converts hyphen/underscore/slash delimitered names into + * camelized classNames. + * + * e.g. my-component => MyComponent + * some_else => SomeElse + * some/comp => SomeComp + * + * @param {String} str + * @return {String} + */ + + var classifyRE = /(?:^|[-_\/])(\w)/g + exports.classify = function (str){ + return str.replace(classifyRE, toUpper) + } + + /** + * Simple bind, faster than native + * + * @param {Function} fn + * @param {Object} ctx + * @return {Function} + */ + + exports.bind = function (fn, ctx){ + return function (a){ + var l = arguments.length + return l + ? l > 1 + ? fn.apply(ctx, arguments) + : fn.call(ctx, a) + : fn.call(ctx) + } + } + + /** + * Convert an Array-like object to a real Array. + * + * @param {Array-like} list + * @param {Number} [start] - start index + * @return {Array} + */ + + exports.toArray = function (list, start){ + start = start || 0 + var i = list.length - start + var ret = new Array(i) + while (i--) { + ret[i] = list[i + start] + } + return ret + } + + /** + * Mix properties into target object. + * + * @param {Object} to + * @param {Object} from + */ + + exports.extend = function (to, from){ + var keys = Object.keys(from) + var i = keys.length + while (i--) { + to[keys[i]] = from[keys[i]] + } + return to + } + + /** + * Quick object check - this is primarily used to tell + * Objects from primitive values when we know the value + * is a JSON-compliant type. + * + * @param {*} obj + * @return {Boolean} + */ + + exports.isObject = function (obj){ + return obj !== null && typeof obj === 'object' + } + + /** + * Strict object type check. Only returns true + * for plain JavaScript objects. + * + * @param {*} obj + * @return {Boolean} + */ + + var toString = Object.prototype.toString + var OBJECT_STRING = '[object Object]' + exports.isPlainObject = function (obj){ + return toString.call(obj) === OBJECT_STRING + } + + /** + * Array type check. + * + * @param {*} obj + * @return {Boolean} + */ + + exports.isArray = Array.isArray + + /** + * Define a non-enumerable property + * + * @param {Object} obj + * @param {String} key + * @param {*} val + * @param {Boolean} [enumerable] + */ + + exports.define = function (obj, key, val, enumerable){ + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }) + } + + /** + * Debounce a function so it only gets called after the + * input stops arriving after the given wait period. + * + * @param {Function} func + * @param {Number} wait + * @return {Function} - the debounced function + */ + + exports.debounce = function (func, wait){ + var timeout, args, context, timestamp, result + var later = function (){ + var last = Date.now() - timestamp + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + return function (){ + context = this + args = arguments + timestamp = Date.now() + if (!timeout) { + timeout = setTimeout(later, wait) + } + return result + } + } + + /** + * Manual indexOf because it's slightly faster than + * native. + * + * @param {Array} arr + * @param {*} obj + */ + + exports.indexOf = function (arr, obj){ + var i = arr.length + while (i--) { + if (arr[i] === obj) return i + } + return -1 + } + + /** + * Make a cancellable version of an async callback. + * + * @param {Function} fn + * @return {Function} + */ + + exports.cancellable = function (fn){ + var cb = function (){ + if (!cb.cancelled) { + return fn.apply(this, arguments) + } + } + cb.cancel = function (){ + cb.cancelled = true + } + return cb + } + + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + * + * @param {*} a + * @param {*} b + * @return {Boolean} + */ + + exports.looseEqual = function (a, b){ + /* eslint-disable eqeqeq */ + return a == b || ( + exports.isObject(a) && exports.isObject(b) + ? JSON.stringify(a) === JSON.stringify(b) + : false + ) + /* eslint-enable eqeqeq */ + } + + /***/ + }, + /* 3 */ + /***/ function (module, exports){ + + // can we use __proto__? + exports.hasProto = '__proto__' in {} + + // Browser environment sniffing + var inBrowser = exports.inBrowser = + typeof window !== 'undefined' && + Object.prototype.toString.call(window) !== '[object Object]' + + exports.isIE9 = + inBrowser && + navigator.userAgent.toLowerCase().indexOf('msie 9.0') > 0 + + exports.isAndroid = + inBrowser && + navigator.userAgent.toLowerCase().indexOf('android') > 0 + + // Transition property/event sniffing + if (inBrowser && !exports.isIE9) { + var isWebkitTrans = + window.ontransitionend === undefined && + window.onwebkittransitionend !== undefined + var isWebkitAnim = + window.onanimationend === undefined && + window.onwebkitanimationend !== undefined + exports.transitionProp = isWebkitTrans + ? 'WebkitTransition' + : 'transition' + exports.transitionEndEvent = isWebkitTrans + ? 'webkitTransitionEnd' + : 'transitionend' + exports.animationProp = isWebkitAnim + ? 'WebkitAnimation' + : 'animation' + exports.animationEndEvent = isWebkitAnim + ? 'webkitAnimationEnd' + : 'animationend' + } + + /** + * Defer a task to execute it asynchronously. Ideally this + * should be executed as a microtask, so we leverage + * MutationObserver if it's available, and fallback to + * setTimeout(0). + * + * @param {Function} cb + * @param {Object} ctx + */ + + exports.nextTick = (function (){ + var callbacks = [] + var pending = false + var timerFunc + + function nextTickHandler(){ + pending = false + var copies = callbacks.slice(0) + callbacks = [] + for (var i = 0; i < copies.length; i++) { + copies[i]() + } + } + + /* istanbul ignore if */ + if (typeof MutationObserver !== 'undefined') { + var counter = 1 + var observer = new MutationObserver(nextTickHandler) + var textNode = document.createTextNode(counter) + observer.observe(textNode, { + characterData: true + }) + timerFunc = function (){ + counter = (counter + 1) % 2 + textNode.data = counter + } + } else { + timerFunc = setTimeout + } + return function (cb, ctx){ + var func = ctx + ? function (){ cb.call(ctx) } + : cb + callbacks.push(func) + if (pending) return + pending = true + timerFunc(nextTickHandler, 0) + } + })() + + /***/ + }, + /* 4 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var config = __webpack_require__(5) + var transition = __webpack_require__(9) + + /** + * Query an element selector if it's not an element already. + * + * @param {String|Element} el + * @return {Element} + */ + + exports.query = function (el){ + if (typeof el === 'string') { + var selector = el + el = document.querySelector(el) + if (!el) { + ("development") !== 'production' && _.warn( + 'Cannot find element: ' + selector + ) + } + } + return el + } + + /** + * Check if a node is in the document. + * Note: document.documentElement.contains should work here + * but always returns false for comment nodes in phantomjs, + * making unit tests difficult. This is fixed by doing the + * contains() check on the node's parentNode instead of + * the node itself. + * + * @param {Node} node + * @return {Boolean} + */ + + exports.inDoc = function (node){ + var doc = document.documentElement + var parent = node && node.parentNode + return doc === node || + doc === parent || !!(parent && parent.nodeType === 1 && (doc.contains(parent))) + } + + /** + * Get and remove an attribute from a node. + * + * @param {Node} node + * @param {String} attr + */ + + exports.attr = function (node, attr){ + var val = node.getAttribute(attr) + if (val !== null) { + node.removeAttribute(attr) + } + return val + } + + /** + * Get an attribute with colon or v-bind: prefix. + * + * @param {Node} node + * @param {String} name + * @return {String|null} + */ + + exports.getBindAttr = function (node, name){ + var val = exports.attr(node, ':' + name) + if (val === null) { + val = exports.attr(node, 'v-bind:' + name) + } + return val + } + + /** + * Insert el before target + * + * @param {Element} el + * @param {Element} target + */ + + exports.before = function (el, target){ + target.parentNode.insertBefore(el, target) + } + + /** + * Insert el after target + * + * @param {Element} el + * @param {Element} target + */ + + exports.after = function (el, target){ + if (target.nextSibling) { + exports.before(el, target.nextSibling) + } else { + target.parentNode.appendChild(el) + } + } + + /** + * Remove el from DOM + * + * @param {Element} el + */ + + exports.remove = function (el){ + el.parentNode.removeChild(el) + } + + /** + * Prepend el to target + * + * @param {Element} el + * @param {Element} target + */ + + exports.prepend = function (el, target){ + if (target.firstChild) { + exports.before(el, target.firstChild) + } else { + target.appendChild(el) + } + } + + /** + * Replace target with el + * + * @param {Element} target + * @param {Element} el + */ + + exports.replace = function (target, el){ + var parent = target.parentNode + if (parent) { + parent.replaceChild(el, target) + } + } + + /** + * Add event listener shorthand. + * + * @param {Element} el + * @param {String} event + * @param {Function} cb + */ + + exports.on = function (el, event, cb){ + el.addEventListener(event, cb) + } + + /** + * Remove event listener shorthand. + * + * @param {Element} el + * @param {String} event + * @param {Function} cb + */ + + exports.off = function (el, event, cb){ + el.removeEventListener(event, cb) + } + + /** + * Add class with compatibility for IE & SVG + * + * @param {Element} el + * @param {Strong} cls + */ + + exports.addClass = function (el, cls){ + if (el.classList) { + el.classList.add(cls) + } else { + var cur = ' ' + (el.getAttribute('class') || '') + ' ' + if (cur.indexOf(' ' + cls + ' ') < 0) { + el.setAttribute('class', (cur + cls).trim()) + } + } + } + + /** + * Remove class with compatibility for IE & SVG + * + * @param {Element} el + * @param {Strong} cls + */ + + exports.removeClass = function (el, cls){ + if (el.classList) { + el.classList.remove(cls) + } else { + var cur = ' ' + (el.getAttribute('class') || '') + ' ' + var tar = ' ' + cls + ' ' + while (cur.indexOf(tar) >= 0) { + cur = cur.replace(tar, ' ') + } + el.setAttribute('class', cur.trim()) + } + if (!el.className) { + el.removeAttribute('class') + } + } + + /** + * Extract raw content inside an element into a temporary + * container div + * + * @param {Element} el + * @param {Boolean} asFragment + * @return {Element} + */ + + exports.extractContent = function (el, asFragment){ + var child + var rawContent + /* istanbul ignore if */ + if ( + exports.isTemplate(el) && + el.content instanceof DocumentFragment + ) { + el = el.content + } + if (el.hasChildNodes()) { + exports.trimNode(el) + rawContent = asFragment + ? document.createDocumentFragment() + : document.createElement('div') + /* eslint-disable no-cond-assign */ + while (child = el.firstChild) { + /* eslint-enable no-cond-assign */ + rawContent.appendChild(child) + } + } + return rawContent + } + + /** + * Trim possible empty head/tail textNodes inside a parent. + * + * @param {Node} node + */ + + exports.trimNode = function (node){ + trim(node, node.firstChild) + trim(node, node.lastChild) + } + + function trim(parent, node){ + if (node && node.nodeType === 3 && !node.data.trim()) { + parent.removeChild(node) + } + } + + /** + * Check if an element is a template tag. + * Note if the template appears inside an SVG its tagName + * will be in lowercase. + * + * @param {Element} el + */ + + exports.isTemplate = function (el){ + return el.tagName && + el.tagName.toLowerCase() === 'template' + } + + /** + * Create an "anchor" for performing dom insertion/removals. + * This is used in a number of scenarios: + * - fragment instance + * - v-html + * - v-if + * - v-for + * - component + * + * @param {String} content + * @param {Boolean} persist - IE trashes empty textNodes on + * cloneNode(true), so in certain + * cases the anchor needs to be + * non-empty to be persisted in + * templates. + * @return {Comment|Text} + */ + + exports.createAnchor = function (content, persist){ + var anchor = config.debug + ? document.createComment(content) + : document.createTextNode(persist ? ' ' : '') + anchor.__vue_anchor = true + return anchor + } + + /** + * Find a component ref attribute that starts with $. + * + * @param {Element} node + * @return {String|undefined} + */ + + var refRE = /^v-ref:/ + exports.findRef = function (node){ + if (node.hasAttributes()) { + var attrs = node.attributes + for (var i = 0, l = attrs.length; i < l; i++) { + var name = attrs[i].name + if (refRE.test(name)) { + return _.camelize(name.replace(refRE, '')) + } + } + } + } + + /** + * Map a function to a range of nodes . + * + * @param {Node} node + * @param {Node} end + * @param {Function} op + */ + + exports.mapNodeRange = function (node, end, op){ + var next + while (node !== end) { + next = node.nextSibling + op(node) + node = next + } + op(end) + } + + /** + * Remove a range of nodes with transition, store + * the nodes in a fragment with correct ordering, + * and call callback when done. + * + * @param {Node} start + * @param {Node} end + * @param {Vue} vm + * @param {DocumentFragment} frag + * @param {Function} cb + */ + + exports.removeNodeRange = function (start, end, vm, frag, cb){ + var done = false + var removed = 0 + var nodes = [] + exports.mapNodeRange(start, end, function (node){ + if (node === end) done = true + nodes.push(node) + transition.remove(node, vm, onRemoved) + }) + function onRemoved(){ + removed++ + if (done && removed >= nodes.length) { + for (var i = 0; i < nodes.length; i++) { + frag.appendChild(nodes[i]) + } + cb && cb() + } + } + } + + /***/ + }, + /* 5 */ + /***/ function (module, exports, __webpack_require__){ + + module.exports = { + + /** + * Whether to print debug messages. + * Also enables stack trace for warnings. + * + * @type {Boolean} + */ + + debug: false, + + /** + * Whether to suppress warnings. + * + * @type {Boolean} + */ + + silent: false, + + /** + * Whether to use async rendering. + */ + + async: true, + + /** + * Whether to warn against errors caught when evaluating + * expressions. + */ + + warnExpressionErrors: true, + + /** + * Whether or not to handle fully object properties which + * are already backed by getters and seters. Depending on + * use case and environment, this might introduce non-neglible + * performance penalties. + */ + convertAllProperties: false, + + /** + * Internal flag to indicate the delimiters have been + * changed. + * + * @type {Boolean} + */ + + _delimitersChanged: true, + + /** + * List of asset types that a component can own. + * + * @type {Array} + */ + + _assetTypes: [ + 'component', + 'directive', + 'elementDirective', + 'filter', + 'transition', + 'partial' + ], + + /** + * prop binding modes + */ + + _propBindingModes: { + ONE_WAY: 0, + TWO_WAY: 1, + ONE_TIME: 2 + }, + + /** + * Max circular updates allowed in a batcher flush cycle. + */ + + _maxUpdateCount: 100 + + } + + /** + * Interpolation delimiters. Changing these would trigger + * the text parser to re-compile the regular expressions. + * + * @type {Array} + */ + + var delimiters = ['{{', '}}'] + var unsafeDelimiters = ['{{{', '}}}'] + var textParser = __webpack_require__(6) + + Object.defineProperty(module.exports, 'delimiters', { + get: function (){ + return delimiters + }, + set: function (val){ + delimiters = val + textParser.compileRegex() + } + }) + + Object.defineProperty(module.exports, 'unsafeDelimiters', { + get: function (){ + return unsafeDelimiters + }, + set: function (val){ + unsafeDelimiters = val + textParser.compileRegex() + } + }) + + /***/ + }, + /* 6 */ + /***/ function (module, exports, __webpack_require__){ + + var Cache = __webpack_require__(7) + var config = __webpack_require__(5) + var dirParser = __webpack_require__(8) + var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g + var cache, tagRE, htmlRE + + /** + * Escape a string so it can be used in a RegExp + * constructor. + * + * @param {String} str + */ + + function escapeRegex(str){ + return str.replace(regexEscapeRE, '\\$&') + } + + exports.compileRegex = function (){ + var open = escapeRegex(config.delimiters[0]) + var close = escapeRegex(config.delimiters[1]) + var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]) + var unsafeClose = escapeRegex(config.unsafeDelimiters[1]) + tagRE = new RegExp( + unsafeOpen + '(.+?)' + unsafeClose + '|' + + open + '(.+?)' + close, + 'g' + ) + htmlRE = new RegExp( + '^' + unsafeOpen + '.*' + unsafeClose + '$' + ) + // reset cache + cache = new Cache(1000) + } + + /** + * Parse a template text string into an array of tokens. + * + * @param {String} text + * @return {Array | null} + * - {String} type + * - {String} value + * - {Boolean} [html] + * - {Boolean} [oneTime] + */ + + exports.parse = function (text){ + if (!cache) { + exports.compileRegex() + } + var hit = cache.get(text) + if (hit) { + return hit + } + text = text.replace(/\n/g, '') + if (!tagRE.test(text)) { + return null + } + var tokens = [] + var lastIndex = tagRE.lastIndex = 0 + var match, index, html, value, first, oneTime + /* eslint-disable no-cond-assign */ + while (match = tagRE.exec(text)) { + /* eslint-enable no-cond-assign */ + index = match.index + // push text token + if (index > lastIndex) { + tokens.push({ + value: text.slice(lastIndex, index) + }) + } + // tag token + html = htmlRE.test(match[0]) + value = html ? match[1] : match[2] + first = value.charCodeAt(0) + oneTime = first === 42 // * + value = oneTime + ? value.slice(1) + : value + tokens.push({ + tag: true, + value: value.trim(), + html: html, + oneTime: oneTime + }) + lastIndex = index + match[0].length + } + if (lastIndex < text.length) { + tokens.push({ + value: text.slice(lastIndex) + }) + } + cache.put(text, tokens) + return tokens + } + + /** + * Format a list of tokens into an expression. + * e.g. tokens parsed from 'a {{b}} c' can be serialized + * into one single expression as '"a " + b + " c"'. + * + * @param {Array} tokens + * @return {String} + */ + + exports.tokensToExp = function (tokens){ + if (tokens.length > 1) { + return tokens.map(function (token){ + return formatToken(token) + }).join('+') + } else { + return formatToken(tokens[0], true) + } + } + + /** + * Format a single token. + * + * @param {Object} token + * @param {Boolean} single + * @return {String} + */ + + function formatToken(token, single){ + return token.tag + ? inlineFilters(token.value, single) + : '"' + token.value + '"' + } + + /** + * For an attribute with multiple interpolation tags, + * e.g. attr="some-{{thing | filter}}", in order to combine + * the whole thing into a single watchable expression, we + * have to inline those filters. This function does exactly + * that. This is a bit hacky but it avoids heavy changes + * to directive parser and watcher mechanism. + * + * @param {String} exp + * @param {Boolean} single + * @return {String} + */ + + var filterRE = /[^|]\|[^|]/ + + function inlineFilters(exp, single){ + if (!filterRE.test(exp)) { + return single + ? exp + : '(' + exp + ')' + } else { + var dir = dirParser.parse(exp) + if (!dir.filters) { + return '(' + exp + ')' + } else { + return 'this._applyFilters(' + + dir.expression + // value + ',null,' + // oldValue (null for read) + JSON.stringify(dir.filters) + // filter descriptors + ',false)' // write? + } + } + } + + /***/ + }, + /* 7 */ + /***/ function (module, exports){ + + /** + * A doubly linked list-based Least Recently Used (LRU) + * cache. Will keep most recently used items while + * discarding least recently used items when its limit is + * reached. This is a bare-bone version of + * Rasmus Andersson's js-lru: + * + * https://github.com/rsms/js-lru + * + * @param {Number} limit + * @constructor + */ + + function Cache(limit){ + this.size = 0 + this.limit = limit + this.head = this.tail = undefined + this._keymap = Object.create(null) + } + + var p = Cache.prototype + + /** + * Put into the cache associated with . + * Returns the entry which was removed to make room for + * the new entry. Otherwise undefined is returned. + * (i.e. if there was enough room already). + * + * @param {String} key + * @param {*} value + * @return {Entry|undefined} + */ + + p.put = function (key, value){ + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + this.tail.newer = entry + entry.older = this.tail + } else { + this.head = entry + } + this.tail = entry + if (this.size === this.limit) { + return this.shift() + } else { + this.size++ + } + } + + /** + * Purge the least recently used (oldest) entry from the + * cache. Returns the removed entry or undefined if the + * cache was empty. + */ + + p.shift = function (){ + var entry = this.head + if (entry) { + this.head = this.head.newer + this.head.older = undefined + entry.newer = entry.older = undefined + this._keymap[entry.key] = undefined + } + return entry + } + + /** + * Get and register recent use of . Returns the value + * associated with or undefined if not in cache. + * + * @param {String} key + * @param {Boolean} returnEntry + * @return {Entry|*} + */ + + p.get = function (key, returnEntry){ + var entry = this._keymap[key] + if (entry === undefined) return + if (entry === this.tail) { + return returnEntry + ? entry + : entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer + } + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer // C. --> E + } + entry.newer = undefined // D --x + entry.older = this.tail // D. --> E + if (this.tail) { + this.tail.newer = entry // E. <-- D + } + this.tail = entry + return returnEntry + ? entry + : entry.value + } + + module.exports = Cache + + /***/ + }, + /* 8 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var Cache = __webpack_require__(7) + var cache = new Cache(1000) + var filterTokenRE = /[^\s'"]+|'[^']*'|"[^"]*"/g + var reservedArgRE = /^in$|^-?\d+/ + + /** + * Parser state + */ + + var str, dir + var c, i, l, lastFilterIndex + var inSingle, inDouble, curly, square, paren + + /** + * Push a filter to the current directive object + */ + + function pushFilter(){ + var exp = str.slice(lastFilterIndex, i).trim() + var filter + if (exp) { + filter = {} + var tokens = exp.match(filterTokenRE) + filter.name = tokens[0] + if (tokens.length > 1) { + filter.args = tokens.slice(1).map(processFilterArg) + } + } + if (filter) { + (dir.filters = dir.filters || []).push(filter) + } + lastFilterIndex = i + 1 + } + + /** + * Check if an argument is dynamic and strip quotes. + * + * @param {String} arg + * @return {Object} + */ + + function processFilterArg(arg){ + if (reservedArgRE.test(arg)) { + return { + value: _.toNumber(arg), + dynamic: false + } + } else { + var stripped = _.stripQuotes(arg) + var dynamic = stripped === arg + return { + value: dynamic ? arg : stripped, + dynamic: dynamic + } + } + } + + /** + * Parse a directive value and extract the expression + * and its filters into a descriptor. + * + * Example: + * + * "a + 1 | uppercase" will yield: + * { + * expression: 'a + 1', + * filters: [ + * { name: 'uppercase', args: null } + * ] + * } + * + * @param {String} str + * @return {Object} + */ + + exports.parse = function (s){ + + var hit = cache.get(s) + if (hit) { + return hit + } + + // reset parser state + str = s + inSingle = inDouble = false + curly = square = paren = 0 + lastFilterIndex = 0 + dir = {} + + for (i = 0, l = str.length; i < l; i++) { + c = str.charCodeAt(i) + if (inSingle) { + // check single quote + if (c === 0x27) inSingle = !inSingle + } else if (inDouble) { + // check double quote + if (c === 0x22) inDouble = !inDouble + } else if ( + c === 0x7C && // pipe + str.charCodeAt(i + 1) !== 0x7C && + str.charCodeAt(i - 1) !== 0x7C + ) { + if (dir.expression == null) { + // first filter, end of expression + lastFilterIndex = i + 1 + dir.expression = str.slice(0, i).trim() + } else { + // already has filter + pushFilter() + } + } else { + switch (c) { + case 0x22: + inDouble = true; + break // " + case 0x27: + inSingle = true; + break // ' + case 0x28: + paren++; + break // ( + case 0x29: + paren--; + break // ) + case 0x5B: + square++; + break // [ + case 0x5D: + square--; + break // ] + case 0x7B: + curly++; + break // { + case 0x7D: + curly--; + break // } + } + } + } + + if (dir.expression == null) { + dir.expression = str.slice(0, i).trim() + } else if (lastFilterIndex !== 0) { + pushFilter() + } + + cache.put(s, dir) + return dir + } + + /***/ + }, + /* 9 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + + /** + * Append with transition. + * + * @param {Element} el + * @param {Element} target + * @param {Vue} vm + * @param {Function} [cb] + */ + + exports.append = function (el, target, vm, cb){ + apply(el, 1, function (){ + target.appendChild(el) + }, vm, cb) + } + + /** + * InsertBefore with transition. + * + * @param {Element} el + * @param {Element} target + * @param {Vue} vm + * @param {Function} [cb] + */ + + exports.before = function (el, target, vm, cb){ + apply(el, 1, function (){ + _.before(el, target) + }, vm, cb) + } + + /** + * Remove with transition. + * + * @param {Element} el + * @param {Vue} vm + * @param {Function} [cb] + */ + + exports.remove = function (el, vm, cb){ + apply(el, -1, function (){ + _.remove(el) + }, vm, cb) + } + + /** + * Apply transitions with an operation callback. + * + * @param {Element} el + * @param {Number} direction + * 1: enter + * -1: leave + * @param {Function} op - the actual DOM operation + * @param {Vue} vm + * @param {Function} [cb] + */ + + var apply = exports.apply = function (el, direction, op, vm, cb){ + var transition = el.__v_trans + if ( + !transition || + // skip if there are no js hooks and CSS transition is + // not supported + (!transition.hooks && !_.transitionEndEvent) || + // skip transitions for initial compile + !vm._isCompiled || + // if the vm is being manipulated by a parent directive + // during the parent's compilation phase, skip the + // animation. + (vm.$parent && !vm.$parent._isCompiled) + ) { + op() + if (cb) cb() + return + } + var action = direction > 0 ? 'enter' : 'leave' + transition[action](op, cb) + } + + /***/ + }, + /* 10 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var config = __webpack_require__(5) + var extend = _.extend + + /** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + * + * All strategy functions follow the same signature: + * + * @param {*} parentVal + * @param {*} childVal + * @param {Vue} [vm] + */ + + var strats = config.optionMergeStrategies = Object.create(null) + + /** + * Helper that recursively merges two data objects together. + */ + + function mergeData(to, from){ + var key, toVal, fromVal + for (key in from) { + toVal = to[key] + fromVal = from[key] + if (!to.hasOwnProperty(key)) { + _.set(to, key, fromVal) + } else if (_.isObject(toVal) && _.isObject(fromVal)) { + mergeData(toVal, fromVal) + } + } + return to + } + + /** + * Data + */ + + strats.data = function (parentVal, childVal, vm){ + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal + } + if (typeof childVal !== 'function') { + ("development") !== 'production' && _.warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.' + ) + return parentVal + } + if (!parentVal) { + return childVal + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn(){ + return mergeData( + childVal.call(this), + parentVal.call(this) + ) + } + } else if (parentVal || childVal) { + return function mergedInstanceDataFn(){ + // instance merge + var instanceData = typeof childVal === 'function' + ? childVal.call(vm) + : childVal + var defaultData = typeof parentVal === 'function' + ? parentVal.call(vm) + : undefined + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } + } + + /** + * El + */ + + strats.el = function (parentVal, childVal, vm){ + if (!vm && childVal && typeof childVal !== 'function') { + ("development") !== 'production' && _.warn( + 'The "el" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.' + ) + return + } + var ret = childVal || parentVal + // invoke the element factory if this is instance merge + return vm && typeof ret === 'function' + ? ret.call(vm) + : ret + } + + /** + * Hooks and param attributes are merged as arrays. + */ + + strats.init = + strats.created = + strats.ready = + strats.attached = + strats.detached = + strats.beforeCompile = + strats.compiled = + strats.beforeDestroy = + strats.destroyed = function (parentVal, childVal){ + return childVal + ? parentVal + ? parentVal.concat(childVal) + : _.isArray(childVal) + ? childVal + : [childVal] + : parentVal + } + + /** + * 0.11 deprecation warning + */ + + strats.paramAttributes = function (){ + /* istanbul ignore next */ + ("development") !== 'production' && _.warn( + '"paramAttributes" option has been deprecated in 0.12. ' + + 'Use "props" instead.' + ) + } + + /** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ + + function mergeAssets(parentVal, childVal){ + var res = Object.create(parentVal) + return childVal + ? extend(res, guardArrayAssets(childVal)) + : res + } + + config._assetTypes.forEach(function (type){ + strats[type + 's'] = mergeAssets + }) + + /** + * Events & Watchers. + * + * Events & watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ + + strats.watch = + strats.events = function (parentVal, childVal){ + if (!childVal) return parentVal + if (!parentVal) return childVal + var ret = {} + extend(ret, parentVal) + for (var key in childVal) { + var parent = ret[key] + var child = childVal[key] + if (parent && !_.isArray(parent)) { + parent = [parent] + } + ret[key] = parent + ? parent.concat(child) + : [child] + } + return ret + } + + /** + * Other object hashes. + */ + + strats.props = + strats.methods = + strats.computed = function (parentVal, childVal){ + if (!childVal) return parentVal + if (!parentVal) return childVal + var ret = Object.create(null) + extend(ret, parentVal) + extend(ret, childVal) + return ret + } + + /** + * Default strategy. + */ + + var defaultStrat = function (parentVal, childVal){ + return childVal === undefined + ? parentVal + : childVal + } + + /** + * Make sure component options get converted to actual + * constructors. + * + * @param {Object} options + */ + + function guardComponents(options){ + if (options.components) { + var components = options.components = + guardArrayAssets(options.components) + var def + var ids = Object.keys(components) + for (var i = 0, l = ids.length; i < l; i++) { + var key = ids[i] + if (_.commonTagRE.test(key)) { + ("development") !== 'production' && _.warn( + 'Do not use built-in HTML elements as component ' + + 'id: ' + key + ) + continue + } + def = components[key] + if (_.isPlainObject(def)) { + components[key] = _.Vue.extend(def) + } + } + } + } + + /** + * Ensure all props option syntax are normalized into the + * Object-based format. + * + * @param {Object} options + */ + + function guardProps(options){ + var props = options.props + var i, val + if (_.isArray(props)) { + options.props = {} + i = props.length + while (i--) { + val = props[i] + if (typeof val === 'string') { + options.props[val] = null + } else if (val.name) { + options.props[val.name] = val + } + } + } else if (_.isPlainObject(props)) { + var keys = Object.keys(props) + i = keys.length + while (i--) { + val = props[keys[i]] + if (typeof val === 'function') { + props[keys[i]] = { type: val } + } + } + } + } + + /** + * Guard an Array-format assets option and converted it + * into the key-value Object format. + * + * @param {Object|Array} assets + * @return {Object} + */ + + function guardArrayAssets(assets){ + if (_.isArray(assets)) { + var res = {} + var i = assets.length + var asset + while (i--) { + asset = assets[i] + var id = typeof asset === 'function' + ? ((asset.options && asset.options.name) || asset.id) + : (asset.name || asset.id) + if (!id) { + ("development") !== 'production' && _.warn( + 'Array-syntax assets must provide a "name" or "id" field.' + ) + } else { + res[id] = asset + } + } + return res + } + return assets + } + + /** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + * + * @param {Object} parent + * @param {Object} child + * @param {Vue} [vm] - if vm is present, indicates this is + * an instantiation merge. + */ + + exports.mergeOptions = function merge(parent, child, vm){ + guardComponents(child) + guardProps(child) + var options = {} + var key + if (child.mixins) { + for (var i = 0, l = child.mixins.length; i < l; i++) { + parent = merge(parent, child.mixins[i], vm) + } + } + for (key in parent) { + mergeField(key) + } + for (key in child) { + if (!(parent.hasOwnProperty(key))) { + mergeField(key) + } + } + function mergeField(key){ + var strat = strats[key] || defaultStrat + options[key] = strat(parent[key], child[key], vm, key) + } + + return options + } + + /** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + * + * @param {Object} options + * @param {String} type + * @param {String} id + * @return {Object|Function} + */ + + exports.resolveAsset = function resolve(options, type, id){ + var assets = options[type] + var camelizedId + return assets[id] || + // camelCase ID + assets[camelizedId = _.camelize(id)] || + // Pascal Case ID + assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)] + } + + /***/ + }, + /* 11 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + + /** + * Check if an element is a component, if yes return its + * component id. + * + * @param {Element} el + * @param {Object} options + * @return {Object|undefined} + */ + + exports.commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/ + exports.checkComponent = function (el, options){ + var tag = el.tagName.toLowerCase() + var hasAttrs = el.hasAttributes() + if (!exports.commonTagRE.test(tag) && tag !== 'component') { + if (_.resolveAsset(options, 'components', tag)) { + return { id: tag } + } else { + var is = hasAttrs && getIsBinding(el) + if (is) { + return is + } else if (true) { + if ( + tag.indexOf('-') > -1 || + ( + /HTMLUnknownElement/.test(el.toString()) && + // Chrome returns unknown for several HTML5 elements. + // https://code.google.com/p/chromium/issues/detail?id=540526 + !/^(data|time|rtc|rb)$/.test(tag) + ) + ) { + _.warn( + 'Unknown custom element: <' + tag + '> - did you ' + + 'register the component correctly?' + ) + } + } + } + } else if (hasAttrs) { + return getIsBinding(el) + } + } + + /** + * Get "is" binding from an element. + * + * @param {Element} el + * @return {Object|undefined} + */ + + function getIsBinding(el){ + // dynamic syntax + var exp = _.attr(el, 'is') + if (exp != null) { + return { id: exp } + } else { + exp = _.getBindAttr(el, 'is') + if (exp != null) { + return { id: exp, dynamic: true } + } + } + } + + /** + * Set a prop's initial value on a vm and its data object. + * + * @param {Vue} vm + * @param {Object} prop + * @param {*} value + */ + + exports.initProp = function (vm, prop, value){ + if (exports.assertProp(prop, value)) { + var key = prop.path + vm[key] = vm._data[key] = value + } + } + + /** + * Assert whether a prop is valid. + * + * @param {Object} prop + * @param {*} value + */ + + exports.assertProp = function (prop, value){ + // if a prop is not provided and is not required, + // skip the check. + if (prop.raw === null && !prop.required) { + return true + } + var options = prop.options + var type = options.type + var valid = true + var expectedType + if (type) { + if (type === String) { + expectedType = 'string' + valid = typeof value === expectedType + } else if (type === Number) { + expectedType = 'number' + valid = typeof value === 'number' + } else if (type === Boolean) { + expectedType = 'boolean' + valid = typeof value === 'boolean' + } else if (type === Function) { + expectedType = 'function' + valid = typeof value === 'function' + } else if (type === Object) { + expectedType = 'object' + valid = _.isPlainObject(value) + } else if (type === Array) { + expectedType = 'array' + valid = _.isArray(value) + } else { + valid = value instanceof type + } + } + if (!valid) { + ("development") !== 'production' && _.warn( + 'Invalid prop: type check failed for ' + + prop.path + '="' + prop.raw + '".' + + ' Expected ' + formatType(expectedType) + + ', got ' + formatValue(value) + '.' + ) + return false + } + var validator = options.validator + if (validator) { + if (!validator.call(null, value)) { + ("development") !== 'production' && _.warn( + 'Invalid prop: custom validator check failed for ' + + prop.path + '="' + prop.raw + '"' + ) + return false + } + } + return true + } + + function formatType(val){ + return val + ? val.charAt(0).toUpperCase() + val.slice(1) + : 'custom type' + } + + function formatValue(val){ + return Object.prototype.toString.call(val).slice(8, -1) + } + + /***/ + }, + /* 12 */ + /***/ function (module, exports, __webpack_require__){ + + /** + * Enable debug utilities. + */ + + if (true) { + + var config = __webpack_require__(5) + var hasConsole = typeof console !== 'undefined' + + /** + * Log a message. + * + * @param {String} msg + */ + + exports.log = function (msg){ + if (hasConsole && config.debug) { + console.log('[Vue info]: ' + msg) + } + } + + /** + * We've got a problem here. + * + * @param {String} msg + */ + + exports.warn = function (msg, e){ + if (hasConsole && (!config.silent || config.debug)) { + console.warn('[Vue warn]: ' + msg) + /* istanbul ignore if */ + if (config.debug) { + console.warn((e || new Error('Warning Stack Trace')).stack) + } + } + } + + /** + * Assert asset exists + */ + + exports.assertAsset = function (val, type, id){ + if (!val) { + exports.warn('Failed to resolve ' + type + ': ' + id) + } + } + } + + /***/ + }, + /* 13 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var config = __webpack_require__(5) + + /** + * Expose useful internals + */ + + exports.util = _ + exports.config = config + exports.set = _.set + exports.delete = _.delete + exports.nextTick = _.nextTick + + /** + * The following are exposed for advanced usage / plugins + */ + + exports.compiler = __webpack_require__(14) + exports.FragmentFactory = __webpack_require__(21) + exports.internalDirectives = __webpack_require__(36) + exports.parsers = { + path: __webpack_require__(43), + text: __webpack_require__(6), + template: __webpack_require__(19), + directive: __webpack_require__(8), + expression: __webpack_require__(42) + } + + /** + * Each instance constructor, including Vue, has a unique + * cid. This enables us to create wrapped "child + * constructors" for prototypal inheritance and cache them. + */ + + exports.cid = 0 + var cid = 1 + + /** + * Class inheritance + * + * @param {Object} extendOptions + */ + + exports.extend = function (extendOptions){ + extendOptions = extendOptions || {} + var Super = this + var isFirstExtend = Super.cid === 0 + if (isFirstExtend && extendOptions._Ctor) { + return extendOptions._Ctor + } + var name = extendOptions.name || Super.options.name + var Sub = createClass(name || 'VueComponent') + Sub.prototype = Object.create(Super.prototype) + Sub.prototype.constructor = Sub + Sub.cid = cid++ + Sub.options = _.mergeOptions( + Super.options, + extendOptions + ) + Sub['super'] = Super + // allow further extension + Sub.extend = Super.extend + // create asset registers, so extended classes + // can have their private assets too. + config._assetTypes.forEach(function (type){ + Sub[type] = Super[type] + }) + // enable recursive self-lookup + if (name) { + Sub.options.components[name] = Sub + } + // cache constructor + if (isFirstExtend) { + extendOptions._Ctor = Sub + } + return Sub + } + + /** + * A function that returns a sub-class constructor with the + * given name. This gives us much nicer output when + * logging instances in the console. + * + * @param {String} name + * @return {Function} + */ + + function createClass(name){ + return new Function( + 'return function ' + _.classify(name) + + ' (options) { this._init(options) }' + )() + } + + /** + * Plugin system + * + * @param {Object} plugin + */ + + exports.use = function (plugin){ + /* istanbul ignore if */ + if (plugin.installed) { + return + } + // additional parameters + var args = _.toArray(arguments, 1) + args.unshift(this) + if (typeof plugin.install === 'function') { + plugin.install.apply(plugin, args) + } else { + plugin.apply(null, args) + } + plugin.installed = true + return this + } + + /** + * Apply a global mixin by merging it into the default + * options. + */ + + exports.mixin = function (mixin){ + var Vue = _.Vue + Vue.options = _.mergeOptions(Vue.options, mixin) + } + + /** + * Create asset registration methods with the following + * signature: + * + * @param {String} id + * @param {*} definition + */ + + config._assetTypes.forEach(function (type){ + exports[type] = function (id, definition){ + if (!definition) { + return this.options[type + 's'][id] + } else { + /* istanbul ignore if */ + if (true) { + if (type === 'component' && _.commonTagRE.test(id)) { + _.warn( + 'Do not use built-in HTML elements as component ' + + 'id: ' + id + ) + } + } + if ( + type === 'component' && + _.isPlainObject(definition) + ) { + definition.name = id + definition = _.Vue.extend(definition) + } + this.options[type + 's'][id] = definition + return definition + } + } + }) + + /***/ + }, + /* 14 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + + _.extend(exports, __webpack_require__(15)) + _.extend(exports, __webpack_require__(49)) + + /***/ + }, + /* 15 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var publicDirectives = __webpack_require__(16) + var internalDirectives = __webpack_require__(36) + var compileProps = __webpack_require__(48) + var textParser = __webpack_require__(6) + var dirParser = __webpack_require__(8) + var templateParser = __webpack_require__(19) + var resolveAsset = _.resolveAsset + + // special binding prefixes + var bindRE = /^v-bind:|^:/ + var onRE = /^v-on:|^@/ + var argRE = /:(.*)$/ + var modifierRE = /\.[^\.]+/g + var transitionRE = /^(v-bind:|:)?transition$/ + + // terminal directives + var terminalDirectives = [ + 'for', + 'if' + ] + + // default directive priority + var DEFAULT_PRIORITY = 1000 + + /** + * Compile a template and return a reusable composite link + * function, which recursively contains more link functions + * inside. This top level compile function would normally + * be called on instance root nodes, but can also be used + * for partial compilation if the partial argument is true. + * + * The returned composite link function, when called, will + * return an unlink function that tearsdown all directives + * created during the linking phase. + * + * @param {Element|DocumentFragment} el + * @param {Object} options + * @param {Boolean} partial + * @return {Function} + */ + + exports.compile = function (el, options, partial){ + // link function for the node itself. + var nodeLinkFn = partial || !options._asComponent + ? compileNode(el, options) + : null + // link function for the childNodes + var childLinkFn = + !(nodeLinkFn && nodeLinkFn.terminal) && + el.tagName !== 'SCRIPT' && + el.hasChildNodes() + ? compileNodeList(el.childNodes, options) + : null + + /** + * A composite linker function to be called on a already + * compiled piece of DOM, which instantiates all directive + * instances. + * + * @param {Vue} vm + * @param {Element|DocumentFragment} el + * @param {Vue} [host] - host vm of transcluded content + * @param {Object} [scope] - v-for scope + * @param {Fragment} [frag] - link context fragment + * @return {Function|undefined} + */ + + return function compositeLinkFn(vm, el, host, scope, frag){ + // cache childNodes before linking parent, fix #657 + var childNodes = _.toArray(el.childNodes) + // link + var dirs = linkAndCapture(function compositeLinkCapturer(){ + if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag) + if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag) + }, vm) + return makeUnlinkFn(vm, dirs) + } + } + + /** + * Apply a linker to a vm/element pair and capture the + * directives created during the process. + * + * @param {Function} linker + * @param {Vue} vm + */ + + function linkAndCapture(linker, vm){ + var originalDirCount = vm._directives.length + linker() + var dirs = vm._directives.slice(originalDirCount) + dirs.sort(directiveComparator) + for (var i = 0, l = dirs.length; i < l; i++) { + dirs[i]._bind() + } + return dirs + } + + /** + * Directive priority sort comparator + * + * @param {Object} a + * @param {Object} b + */ + + function directiveComparator(a, b){ + a = a.descriptor.def.priority || DEFAULT_PRIORITY + b = b.descriptor.def.priority || DEFAULT_PRIORITY + return a > b ? -1 : a === b ? 0 : 1 + } + + /** + * Linker functions return an unlink function that + * tearsdown all directives instances generated during + * the process. + * + * We create unlink functions with only the necessary + * information to avoid retaining additional closures. + * + * @param {Vue} vm + * @param {Array} dirs + * @param {Vue} [context] + * @param {Array} [contextDirs] + * @return {Function} + */ + + function makeUnlinkFn(vm, dirs, context, contextDirs){ + return function unlink(destroying){ + teardownDirs(vm, dirs, destroying) + if (context && contextDirs) { + teardownDirs(context, contextDirs) + } + } + } + + /** + * Teardown partial linked directives. + * + * @param {Vue} vm + * @param {Array} dirs + * @param {Boolean} destroying + */ + + function teardownDirs(vm, dirs, destroying){ + var i = dirs.length + while (i--) { + dirs[i]._teardown() + if (!destroying) { + vm._directives.$remove(dirs[i]) + } + } + } + + /** + * Compile link props on an instance. + * + * @param {Vue} vm + * @param {Element} el + * @param {Object} props + * @param {Object} [scope] + * @return {Function} + */ + + exports.compileAndLinkProps = function (vm, el, props, scope){ + var propsLinkFn = compileProps(el, props) + var propDirs = linkAndCapture(function (){ + propsLinkFn(vm, scope) + }, vm) + return makeUnlinkFn(vm, propDirs) + } + + /** + * Compile the root element of an instance. + * + * 1. attrs on context container (context scope) + * 2. attrs on the component template root node, if + * replace:true (child scope) + * + * If this is a fragment instance, we only need to compile 1. + * + * @param {Vue} vm + * @param {Element} el + * @param {Object} options + * @param {Object} contextOptions + * @return {Function} + */ + + exports.compileRoot = function (el, options, contextOptions){ + var containerAttrs = options._containerAttrs + var replacerAttrs = options._replacerAttrs + var contextLinkFn, replacerLinkFn + + // only need to compile other attributes for + // non-fragment instances + if (el.nodeType !== 11) { + // for components, container and replacer need to be + // compiled separately and linked in different scopes. + if (options._asComponent) { + // 2. container attributes + if (containerAttrs && contextOptions) { + contextLinkFn = compileDirectives(containerAttrs, contextOptions) + } + if (replacerAttrs) { + // 3. replacer attributes + replacerLinkFn = compileDirectives(replacerAttrs, options) + } + } else { + // non-component, just compile as a normal element. + replacerLinkFn = compileDirectives(el.attributes, options) + } + } else if (("development") !== 'production' && containerAttrs) { + // warn container directives for fragment instances + var names = containerAttrs + .filter(function (attr){ + // allow vue-loader/vueify scoped css attributes + return attr.name.indexOf('_v-') < 0 && + // allow event listeners + !onRE.test(attr.name) && + // allow slots + attr.name !== 'slot' + }) + .map(function (attr){ + return '"' + attr.name + '"' + }) + if (names.length) { + var plural = names.length > 1 + _.warn( + 'Attribute' + (plural ? 's ' : ' ') + names.join(', ') + + (plural ? ' are' : ' is') + ' ignored on component ' + + '<' + options.el.tagName.toLowerCase() + '> because ' + + 'the component is a fragment instance: ' + + 'http://vuejs.org/guide/components.html#Fragment_Instance' + ) + } + } + + return function rootLinkFn(vm, el, scope){ + // link context scope dirs + var context = vm._context + var contextDirs + if (context && contextLinkFn) { + contextDirs = linkAndCapture(function (){ + contextLinkFn(context, el, null, scope) + }, context) + } + + // link self + var selfDirs = linkAndCapture(function (){ + if (replacerLinkFn) replacerLinkFn(vm, el) + }, vm) + + // return the unlink function that tearsdown context + // container directives. + return makeUnlinkFn(vm, selfDirs, context, contextDirs) + } + } + + /** + * Compile a node and return a nodeLinkFn based on the + * node type. + * + * @param {Node} node + * @param {Object} options + * @return {Function|null} + */ + + function compileNode(node, options){ + var type = node.nodeType + if (type === 1 && node.tagName !== 'SCRIPT') { + return compileElement(node, options) + } else if (type === 3 && node.data.trim()) { + return compileTextNode(node, options) + } else { + return null + } + } + + /** + * Compile an element and return a nodeLinkFn. + * + * @param {Element} el + * @param {Object} options + * @return {Function|null} + */ + + function compileElement(el, options){ + // preprocess textareas. + // textarea treats its text content as the initial value. + // just bind it as an attr directive for value. + if (el.tagName === 'TEXTAREA') { + var tokens = textParser.parse(el.value) + if (tokens) { + el.setAttribute(':value', textParser.tokensToExp(tokens)) + el.value = '' + } + } + var linkFn + var hasAttrs = el.hasAttributes() + // check terminal directives (for & if) + if (hasAttrs) { + linkFn = checkTerminalDirectives(el, options) + } + // check element directives + if (!linkFn) { + linkFn = checkElementDirectives(el, options) + } + // check component + if (!linkFn) { + linkFn = checkComponent(el, options) + } + // normal directives + if (!linkFn && hasAttrs) { + linkFn = compileDirectives(el.attributes, options) + } + return linkFn + } + + /** + * Compile a textNode and return a nodeLinkFn. + * + * @param {TextNode} node + * @param {Object} options + * @return {Function|null} textNodeLinkFn + */ + + function compileTextNode(node, options){ + // skip marked text nodes + if (node._skip) { + return removeText + } + + var tokens = textParser.parse(node.wholeText) + if (!tokens) { + return null + } + + // mark adjacent text nodes as skipped, + // because we are using node.wholeText to compile + // all adjacent text nodes together. This fixes + // issues in IE where sometimes it splits up a single + // text node into multiple ones. + var next = node.nextSibling + while (next && next.nodeType === 3) { + next._skip = true + next = next.nextSibling + } + + var frag = document.createDocumentFragment() + var el, token + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i] + el = token.tag + ? processTextToken(token, options) + : document.createTextNode(token.value) + frag.appendChild(el) + } + return makeTextNodeLinkFn(tokens, frag, options) + } + + /** + * Linker for an skipped text node. + * + * @param {Vue} vm + * @param {Text} node + */ + + function removeText(vm, node){ + _.remove(node) + } + + /** + * Process a single text token. + * + * @param {Object} token + * @param {Object} options + * @return {Node} + */ + + function processTextToken(token, options){ + var el + if (token.oneTime) { + el = document.createTextNode(token.value) + } else { + if (token.html) { + el = document.createComment('v-html') + setTokenType('html') + } else { + // IE will clean up empty textNodes during + // frag.cloneNode(true), so we have to give it + // something here... + el = document.createTextNode(' ') + setTokenType('text') + } + } + function setTokenType(type){ + if (token.descriptor) return + var parsed = dirParser.parse(token.value) + token.descriptor = { + name: type, + def: publicDirectives[type], + expression: parsed.expression, + filters: parsed.filters + } + } + + return el + } + + /** + * Build a function that processes a textNode. + * + * @param {Array} tokens + * @param {DocumentFragment} frag + */ + + function makeTextNodeLinkFn(tokens, frag){ + return function textNodeLinkFn(vm, el, host, scope){ + var fragClone = frag.cloneNode(true) + var childNodes = _.toArray(fragClone.childNodes) + var token, value, node + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i] + value = token.value + if (token.tag) { + node = childNodes[i] + if (token.oneTime) { + value = (scope || vm).$eval(value) + if (token.html) { + _.replace(node, templateParser.parse(value, true)) + } else { + node.data = value + } + } else { + vm._bindDir(token.descriptor, node, host, scope) + } + } + } + _.replace(el, fragClone) + } + } + + /** + * Compile a node list and return a childLinkFn. + * + * @param {NodeList} nodeList + * @param {Object} options + * @return {Function|undefined} + */ + + function compileNodeList(nodeList, options){ + var linkFns = [] + var nodeLinkFn, childLinkFn, node + for (var i = 0, l = nodeList.length; i < l; i++) { + node = nodeList[i] + nodeLinkFn = compileNode(node, options) + childLinkFn = + !(nodeLinkFn && nodeLinkFn.terminal) && + node.tagName !== 'SCRIPT' && + node.hasChildNodes() + ? compileNodeList(node.childNodes, options) + : null + linkFns.push(nodeLinkFn, childLinkFn) + } + return linkFns.length + ? makeChildLinkFn(linkFns) + : null + } + + /** + * Make a child link function for a node's childNodes. + * + * @param {Array} linkFns + * @return {Function} childLinkFn + */ + + function makeChildLinkFn(linkFns){ + return function childLinkFn(vm, nodes, host, scope, frag){ + var node, nodeLinkFn, childrenLinkFn + for (var i = 0, n = 0, l = linkFns.length; i < l; n++) { + node = nodes[n] + nodeLinkFn = linkFns[i++] + childrenLinkFn = linkFns[i++] + // cache childNodes before linking parent, fix #657 + var childNodes = _.toArray(node.childNodes) + if (nodeLinkFn) { + nodeLinkFn(vm, node, host, scope, frag) + } + if (childrenLinkFn) { + childrenLinkFn(vm, childNodes, host, scope, frag) + } + } + } + } + + /** + * Check for element directives (custom elements that should + * be resovled as terminal directives). + * + * @param {Element} el + * @param {Object} options + */ + + function checkElementDirectives(el, options){ + var tag = el.tagName.toLowerCase() + if (_.commonTagRE.test(tag)) return + var def = resolveAsset(options, 'elementDirectives', tag) + if (def) { + return makeTerminalNodeLinkFn(el, tag, '', options, def) + } + } + + /** + * Check if an element is a component. If yes, return + * a component link function. + * + * @param {Element} el + * @param {Object} options + * @return {Function|undefined} + */ + + function checkComponent(el, options){ + var component = _.checkComponent(el, options) + if (component) { + var ref = _.findRef(el) + var descriptor = { + name: 'component', + ref: ref, + expression: component.id, + def: internalDirectives.component, + modifiers: { + literal: !component.dynamic + } + } + var componentLinkFn = function (vm, el, host, scope, frag){ + if (ref) { + _.defineReactive((scope || vm).$refs, ref, null) + } + vm._bindDir(descriptor, el, host, scope, frag) + } + componentLinkFn.terminal = true + return componentLinkFn + } + } + + /** + * Check an element for terminal directives in fixed order. + * If it finds one, return a terminal link function. + * + * @param {Element} el + * @param {Object} options + * @return {Function} terminalLinkFn + */ + + function checkTerminalDirectives(el, options){ + // skip v-pre + if (_.attr(el, 'v-pre') !== null) { + return skip + } + // skip v-else block, but only if following v-if + if (el.hasAttribute('v-else')) { + var prev = el.previousElementSibling + if (prev && prev.hasAttribute('v-if')) { + return skip + } + } + var value, dirName + for (var i = 0, l = terminalDirectives.length; i < l; i++) { + dirName = terminalDirectives[i] + /* eslint-disable no-cond-assign */ + if (value = el.getAttribute('v-' + dirName)) { + return makeTerminalNodeLinkFn(el, dirName, value, options) + } + /* eslint-enable no-cond-assign */ + } + } + + function skip(){} + + skip.terminal = true + + /** + * Build a node link function for a terminal directive. + * A terminal link function terminates the current + * compilation recursion and handles compilation of the + * subtree in the directive. + * + * @param {Element} el + * @param {String} dirName + * @param {String} value + * @param {Object} options + * @param {Object} [def] + * @return {Function} terminalLinkFn + */ + + function makeTerminalNodeLinkFn(el, dirName, value, options, def){ + var parsed = dirParser.parse(value) + var descriptor = { + name: dirName, + expression: parsed.expression, + filters: parsed.filters, + raw: value, + // either an element directive, or if/for + def: def || publicDirectives[dirName] + } + // check ref for v-for and router-view + if (dirName === 'for' || dirName === 'router-view') { + descriptor.ref = _.findRef(el) + } + var fn = function terminalNodeLinkFn(vm, el, host, scope, frag){ + if (descriptor.ref) { + _.defineReactive((scope || vm).$refs, descriptor.ref, null) + } + vm._bindDir(descriptor, el, host, scope, frag) + } + fn.terminal = true + return fn + } + + /** + * Compile the directives on an element and return a linker. + * + * @param {Array|NamedNodeMap} attrs + * @param {Object} options + * @return {Function} + */ + + function compileDirectives(attrs, options){ + var i = attrs.length + var dirs = [] + var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens + while (i--) { + attr = attrs[i] + name = rawName = attr.name + value = rawValue = attr.value + tokens = textParser.parse(value) + // reset arg + arg = null + // check modifiers + modifiers = parseModifiers(name) + name = name.replace(modifierRE, '') + + // attribute interpolations + if (tokens) { + value = textParser.tokensToExp(tokens) + arg = name + pushDir('bind', publicDirectives.bind, true) + // warn against mixing mustaches with v-bind + if (true) { + if (name === 'class' && Array.prototype.some.call(attrs, function (attr){ + return attr.name === ':class' || attr.name === 'v-bind:class' + })) { + _.warn( + 'class="' + rawValue + '": Do not mix mustache interpolation ' + + 'and v-bind for "class" on the same element. Use one or the other.' + ) + } + } + } else + + // special attribute: transition + if (transitionRE.test(name)) { + modifiers.literal = !bindRE.test(name) + pushDir('transition', internalDirectives.transition) + } else + + // event handlers + if (onRE.test(name)) { + arg = name.replace(onRE, '') + pushDir('on', publicDirectives.on) + } else + + // attribute bindings + if (bindRE.test(name)) { + dirName = name.replace(bindRE, '') + if (dirName === 'style' || dirName === 'class') { + pushDir(dirName, internalDirectives[dirName]) + } else { + arg = dirName + pushDir('bind', publicDirectives.bind) + } + } else + + // normal directives + if (name.indexOf('v-') === 0) { + // check arg + arg = (arg = name.match(argRE)) && arg[1] + if (arg) { + name = name.replace(argRE, '') + } + // extract directive name + dirName = name.slice(2) + + // skip v-else (when used with v-show) + if (dirName === 'else') { + continue + } + + dirDef = resolveAsset(options, 'directives', dirName) + + if (true) { + _.assertAsset(dirDef, 'directive', dirName) + } + + if (dirDef) { + pushDir(dirName, dirDef) + } + } + } + + /** + * Push a directive. + * + * @param {String} dirName + * @param {Object|Function} def + * @param {Boolean} [interp] + */ + + function pushDir(dirName, def, interp){ + var parsed = dirParser.parse(value) + dirs.push({ + name: dirName, + attr: rawName, + raw: rawValue, + def: def, + arg: arg, + modifiers: modifiers, + expression: parsed.expression, + filters: parsed.filters, + interp: interp + }) + } + + if (dirs.length) { + return makeNodeLinkFn(dirs) + } + } + + /** + * Parse modifiers from directive attribute name. + * + * @param {String} name + * @return {Object} + */ + + function parseModifiers(name){ + var res = Object.create(null) + var match = name.match(modifierRE) + if (match) { + var i = match.length + while (i--) { + res[match[i].slice(1)] = true + } + } + return res + } + + /** + * Build a link function for all directives on a single node. + * + * @param {Array} directives + * @return {Function} directivesLinkFn + */ + + function makeNodeLinkFn(directives){ + return function nodeLinkFn(vm, el, host, scope, frag){ + // reverse apply because it's sorted low to high + var i = directives.length + while (i--) { + vm._bindDir(directives[i], el, host, scope, frag) + } + } + } + + /***/ + }, + /* 16 */ + /***/ function (module, exports, __webpack_require__){ + + // text & html + exports.text = __webpack_require__(17) + exports.html = __webpack_require__(18) + + // logic control + exports['for'] = __webpack_require__(20) + exports['if'] = __webpack_require__(23) + exports.show = __webpack_require__(24) + + // two-way binding + exports.model = __webpack_require__(25) + + // event handling + exports.on = __webpack_require__(30) + + // attributes + exports.bind = __webpack_require__(31) + + // ref & el + exports.el = __webpack_require__(33) + exports.ref = __webpack_require__(34) + + // cloak + exports.cloak = __webpack_require__(35) + + /***/ + }, + /* 17 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + + module.exports = { + + bind: function (){ + this.attr = this.el.nodeType === 3 + ? 'data' + : 'textContent' + }, + + update: function (value){ + this.el[this.attr] = _.toString(value) + } + } + + /***/ + }, + /* 18 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var templateParser = __webpack_require__(19) + + module.exports = { + + bind: function (){ + // a comment node means this is a binding for + // {{{ inline unescaped html }}} + if (this.el.nodeType === 8) { + // hold nodes + this.nodes = [] + // replace the placeholder with proper anchor + this.anchor = _.createAnchor('v-html') + _.replace(this.el, this.anchor) + } + }, + + update: function (value){ + value = _.toString(value) + if (this.nodes) { + this.swap(value) + } else { + this.el.innerHTML = value + } + }, + + swap: function (value){ + // remove old nodes + var i = this.nodes.length + while (i--) { + _.remove(this.nodes[i]) + } + // convert new value to a fragment + // do not attempt to retrieve from id selector + var frag = templateParser.parse(value, true, true) + // save a reference to these nodes so we can remove later + this.nodes = _.toArray(frag.childNodes) + _.before(frag, this.anchor) + } + } + + /***/ + }, + /* 19 */ + /***/ function (module, exports, __webpack_require__){ + + var _ = __webpack_require__(1) + var Cache = __webpack_require__(7) + var templateCache = new Cache(1000) + var idSelectorCache = new Cache(1000) + + var map = { + _default: [0, '', ''], + legend: [1, '
', '
'], + tr: [2, '', '
'], + col: [ + 2, + '', + '
' + ] + } + + map.td = + map.th = [ + 3, + '', + '
' + ] + + map.option = + map.optgroup = [ + 1, + '' + ] + + map.thead = + map.tbody = + map.colgroup = + map.caption = + map.tfoot = [1, '', '
'] + + map.g = + map.defs = + map.symbol = + map.use = + map.image = + map.text = + map.circle = + map.ellipse = + map.line = + map.path = + map.polygon = + map.polyline = + map.rect = [ + 1, + '', + '' + ] + + /** + * Check if a node is a supported template node with a + * DocumentFragment content. + * + * @param {Node} node + * @return {Boolean} + */ + + function isRealTemplate(node){ + return _.isTemplate(node) && + node.content instanceof DocumentFragment + } + + var tagRE = /<([\w:]+)/ + var entityRE = /&\w+;|&#\d+;|&#x[\dA-F]+;/ + + /** + * Convert a string template to a DocumentFragment. + * Determines correct wrapping by tag types. Wrapping + * strategy found in jQuery & component/domify. + * + * @param {String} templateString + * @return {DocumentFragment} + */ + + function stringToFragment(templateString){ + // try a cache hit first + var hit = templateCache.get(templateString) + if (hit) { + return hit + } + + var frag = document.createDocumentFragment() + var tagMatch = templateString.match(tagRE) + var entityMatch = entityRE.test(templateString) + + if (!tagMatch && !entityMatch) { + // text only, return a single text node. + frag.appendChild( + document.createTextNode(templateString) + ) + } else { + + var tag = tagMatch && tagMatch[1] + var wrap = map[tag] || map._default + var depth = wrap[0] + var prefix = wrap[1] + var suffix = wrap[2] + var node = document.createElement('div') + + node.innerHTML = prefix + templateString.trim() + suffix + while (depth--) { + node = node.lastChild + } + + var child + /* eslint-disable no-cond-assign */ + while (child = node.firstChild) { + /* eslint-enable no-cond-assign */ + frag.appendChild(child) + } + } + + templateCache.put(templateString, frag) + return frag + } + + /** + * Convert a template node to a DocumentFragment. + * + * @param {Node} node + * @return {DocumentFragment} + */ + + function nodeToFragment(node){ + // if its a template tag and the browser supports it, + // its content is already a document fragment. + if (isRealTemplate(node)) { + _.trimNode(node.content) + return node.content + } + // script template + if (node.tagName === 'SCRIPT') { + return stringToFragment(node.textContent) + } + // normal node, clone it to avoid mutating the original + var clone = exports.clone(node) + var frag = document.createDocumentFragment() + var child + /* eslint-disable no-cond-assign */ + while (child = clone.firstChild) { + /* eslint-enable no-cond-assign */ + frag.appendChild(child) + } + _.trimNode(frag) + return frag + } + + // Test for the presence of the Safari template cloning bug + // https://bugs.webkit.org/show_bug.cgi?id=137755 + var hasBrokenTemplate = (function (){ + /* istanbul ignore else */ + if (_.inBrowser) { + var a = document.createElement('div') + a.innerHTML = '' + return !a.cloneNode(true).firstChild.innerHTML + } else { + return false + } + })() + + // Test for IE10/11 textarea placeholder clone bug + var hasTextareaCloneBug = (function (){ + /* istanbul ignore else */ + if (_.inBrowser) { + var t = document.createElement('textarea') + t.placeholder = 't' + return t.cloneNode(true).value === 't' + } else { + return false + } + })() + + /** + * 1. Deal with Safari cloning nested