├── .eslintrc ├── .gitignore ├── Array-Huang-webpack-seed-v2 结构图.png ├── LICENSE ├── README.md ├── core ├── _webpack.dev.config.js ├── _webpack.product.config.js ├── components │ ├── footer │ │ └── html.ejs │ ├── header │ │ └── html.ejs │ ├── side-menu │ │ └── html.ejs │ └── top-nav │ │ └── html.ejs ├── config │ ├── bootstrap.config.js │ ├── bootstrap.config.less │ ├── build-file.config.js │ └── common.config.js ├── dll │ ├── dll.css │ └── dll.js ├── iconfont │ ├── demo.css │ ├── demo.html │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.svg │ ├── iconfont.ttf │ └── iconfont.woff ├── layout │ ├── layout-without-nav │ │ ├── html.ejs │ │ └── html.js │ └── layout │ │ ├── html.ejs │ │ └── html.js ├── less │ ├── base-dir │ │ ├── _badge.less │ │ ├── _button.less │ │ ├── _form.less │ │ ├── _global.less │ │ ├── _iconfont.less │ │ ├── _mixins.less │ │ ├── _panel.less │ │ ├── _table.less │ │ └── _variables.less │ ├── base.less │ └── components-dir │ │ ├── _modal.less │ │ ├── _side-nav.less │ │ ├── _tab.less │ │ └── _top-menu.less ├── libs │ ├── libs.module.js │ └── without-jquery.module.js ├── manifest.json ├── npm-scripts │ └── dll.script.js ├── package.json ├── webpack-config │ ├── base │ │ └── dir-vars.config.js │ ├── inherit │ │ └── plugins.config.js │ ├── module.config.js │ ├── plugins.dev.config.js │ ├── plugins.product.config.js │ ├── resolve.config.js │ └── vendor │ │ ├── eslint.config.js │ │ └── postcss.config.js └── webpack-dll.config.js ├── debug.log ├── example-admin-1 ├── index.html ├── npm-scripts │ └── build.script.js ├── package.json ├── pages │ ├── alert │ │ └── index │ │ │ ├── content.ejs │ │ │ ├── html.js │ │ │ └── page.js │ ├── index │ │ ├── index │ │ │ ├── content.ejs │ │ │ ├── html.js │ │ │ └── page.js │ │ └── login │ │ │ ├── html.js │ │ │ ├── page.js │ │ │ ├── page.less │ │ │ └── templates │ │ │ ├── forget-password-box.html │ │ │ ├── login-box.ejs │ │ │ └── main.ejs │ └── user │ │ ├── edit-password │ │ ├── content.ejs │ │ ├── html.js │ │ └── page.js │ │ └── modify-info │ │ ├── content.ejs │ │ ├── html.js │ │ └── page.js ├── public-resource │ ├── config │ │ ├── build-file.config.js │ │ └── common.config.js │ ├── imgs │ │ └── login-bg.jpg │ └── logic │ │ └── common.page.js ├── webpack-config │ ├── alias.config.js │ ├── base │ │ ├── dir-vars.config.js │ │ └── page-entries.config.js │ ├── entry.config.js │ ├── module │ │ ├── loaders.config.js │ │ └── pre-loaders.config.js │ ├── output.config.js │ └── plugins.config.js ├── webpack.dev.config.js └── webpack.product.config.js ├── example-admin-2 ├── index.html ├── npm-scripts │ └── build.script.js ├── package.json ├── pages │ ├── alert │ │ └── index │ │ │ ├── content.ejs │ │ │ ├── html.js │ │ │ └── page.js │ ├── index │ │ ├── index │ │ │ ├── content.ejs │ │ │ ├── html.js │ │ │ └── page.js │ │ └── login │ │ │ ├── html.js │ │ │ ├── page.js │ │ │ ├── page.less │ │ │ └── templates │ │ │ ├── forget-password-box.html │ │ │ ├── login-box.ejs │ │ │ └── main.ejs │ └── user │ │ ├── edit-password │ │ ├── content.ejs │ │ ├── html.js │ │ └── page.js │ │ └── modify-info │ │ ├── content.ejs │ │ ├── html.js │ │ └── page.js ├── public-resource │ ├── config │ │ ├── build-file.config.js │ │ └── common.config.js │ ├── imgs │ │ └── login-bg.jpg │ └── logic │ │ └── common.page.js ├── webpack-config │ ├── alias.config.js │ ├── base │ │ ├── dir-vars.config.js │ │ └── page-entries.config.js │ ├── entry.config.js │ ├── module │ │ ├── loaders.config.js │ │ └── pre-loaders.config.js │ ├── output.config.js │ └── plugins.config.js ├── webpack.dev.config.js └── webpack.product.config.js ├── npm-scripts └── clean-dirs.script.js ├── package.json └── vendor ├── ie-fix ├── html5shiv.min.js ├── jquery.xdomainrequest.min.js ├── respond-proxy.html ├── respond.min.js ├── respond.proxy.gif ├── respond.proxy.js └── xdomain.all.js └── metisMenu ├── metisMenu.min.css └── metisMenu.min.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "globals": { 4 | // Put things like jQuery, etc 5 | "jQuery": true, 6 | "$": true 7 | }, 8 | "env": { 9 | // I write for browser 10 | "browser": true, 11 | // in CommonJS 12 | //"node": true 13 | // in jQuery 14 | "jquery": true 15 | }, 16 | "rules": { 17 | "no-console": 0, //console 未删除 18 | "no-var": 0, 19 | "no-new": 0, 20 | "import/no-unresolved": 0, 21 | "no-underscore-dangle": [0], 22 | "prefer-template": 0, 23 | "object-shorthand": [0], 24 | "prefer-arrow-callback": 0, 25 | "prefer-rest-params": 0, 26 | "no-param-reassign": 0, 27 | "max-len": ["error", { 28 | "code": 200, 29 | "tabWidth": 2, 30 | "comments": 200, 31 | "ignoreComments": false, 32 | "ignoreTrailingComments": false, 33 | "ignoreUrls": true 34 | }], 35 | "global-require": 0, 36 | "no-alert": 0, 37 | 'no-restricted-syntax': [ 38 | 2, 39 | 'LabeledStatement', 40 | 'WithStatement', 41 | ] 42 | } 43 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | node_modules 11 | build 12 | 13 | # Windows Installer files 14 | *.cab 15 | *.msi 16 | *.msm 17 | *.msp 18 | 19 | # Windows shortcuts 20 | *.lnk 21 | 22 | *.sublime-project 23 | *.sublime-workspace -------------------------------------------------------------------------------- /Array-Huang-webpack-seed-v2 结构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/Array-Huang-webpack-seed-v2 结构图.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Array Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webpack-seed-v2 2 | 3 | ## 注意了,本项目不再维护,因为作者我还是打算做回一条复制粘贴开新项目的咸鱼了,请前往[本项目的单项目版本 —— webpack-seed](https://github.com/Array-Huang/webpack-seed),谢谢。 4 | 5 | ## 项目介绍 6 | 本项目是一个利用webpack架构的**web app**脚手架,其特点如下: 7 | - 多个项目可以共用同一套架构/基础设施。 8 | - 更适合**多页应用**。 9 | - 既可实现全后端分离,也可以生成后端渲染所需要的模板。 10 | - 引入layout和component的概念,方便多页面间对布局、组件的重用,妈妈再也不用担心我是选SPA呢还是Iframe了,咱们都!不!需!要! 11 | - 编译后的程序不依赖于外部的资源(包括css、font、图片等资源都做了迁移),可以整体放到CDN上。 12 | - 已整合兼容IE8+的[跨域方案](https://github.com/jpillora/xdomain)。 13 | - 整合Bootstrap3(利用webpack按需打包)及主题SB-Admin,但其实换掉也很简单,或者干脆不用CSS框架也行。 14 | - 不含Js框架(jQuery不算框架,谢谢)。在我原本的项目中,是用avalon2作为Js框架的,但考虑到脚手架本身并不需要Js框架,同时我也希望这个项目保持精简,因此决定剔除掉avalon2的部分。 15 | - 整合[iconfont](http://www.iconfont.cn/)作为字体图标方案,需要什么图标就自己上iconfont那打包下载下来,替换掉`core/iconfont`内的文件。 16 | - 本项目基于**webpack v1**和**webpack-dev-server v1**,全局和项目局部依赖都一样。 17 | 18 | 19 | ## 使用说明 20 | - 本项目使用包管理工具NPM,因此需要先把本项目所依赖的包下载下来: 21 | 22 | ```bash 23 | $ npm install 24 | ``` 25 | 26 | - 进入到示例项目**example-admin-1**目录里(与项目相关的npm scripts都定义在项目内部的`package.json`里了) 27 | 28 | ```bash 29 | $ cd ./example-admin-1 30 | ``` 31 | 32 | - 编译程序,生成的所有代码在`build`目录内。 33 | 34 | ```bash 35 | $ npm run build # 生成生产环境的代码。用npm run watch或npm run dev可生成开发环境的代码 36 | ``` 37 | 38 | - 启动服务器,推荐直接使用webpack-dev-server 39 | 40 | ```bash 41 | $ npm run start 42 | ``` 43 | 44 | - 打开浏览器,在地址栏里输入`http://localhost:8080`,Duang!页面就出来了! 45 | 46 | ## CLI命令(npm scripts) 47 | 48 | ### 在具体项目的目录(如`example-admin-1`目录)中执行的CLI命令 49 | | 命令 | 作用&效果 | 50 | | --------------- | ------------- | 51 | | npm run build | 根据`webpack.config.js`,build出一份生产环境的代码 | 52 | | npm run dev | 根据`webpack.dev.config.js`,build出一份开发环境的代码 | 53 | | npm run watch | 在`npm run dev`的基础上添加`-- watch`,实时监控源文件,建议开发时使用这项 | 54 | | npm run start | 开启webpack-dev-server,然后就可以在 http://localhost:8080/ 查看成品了 | 55 | | npm run profile | 显示编译过程中每一项资源的耗时,用来调优的 | 56 | 57 | 58 | ### 在基础设施/架构核心(`core`目录)中执行的CLI命令 59 | | 命令 | 作用&效果 | 60 | | --------------- | ------------- | 61 | | npm run dll | 生成Dll文件,每次升级第三方库时都需要重新执行一遍 | 62 | 63 | ## 更新日志 64 | 65 | ### 2.0.0 (2016-10-21) 66 | 在1.x版本的基础上抽离出基础设施,以达到多个项目**共用同一套架构/基础设施**的目的。 67 | 68 | ### 1.x 69 | 本脚手架项目的1.x版本([Array-Huang/webpack-seed](https://github.com/Array-Huang/webpack-seed)),因为架构比较简单,所以也比较好上手,但无法实现多项目共用同一套架构/基础设施。 70 | -------------------------------------------------------------------------------- /core/_webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | /* 整理基础设施的webpack配置,供具体项目继承 */ 2 | module.exports = { 3 | module: require('./webpack-config/module.config.js'), 4 | resolve: require('./webpack-config/resolve.config.js'), 5 | plugins: require('./webpack-config/plugins.dev.config.js'), 6 | eslint: require('./webpack-config/vendor/eslint.config.js'), 7 | postcss: require('./webpack-config/vendor/postcss.config.js'), 8 | }; 9 | -------------------------------------------------------------------------------- /core/_webpack.product.config.js: -------------------------------------------------------------------------------- 1 | /* 整理基础设施的webpack配置,供具体项目继承 */ 2 | module.exports = { 3 | module: require('./webpack-config/module.config.js'), 4 | resolve: require('./webpack-config/resolve.config.js'), 5 | plugins: require('./webpack-config/plugins.product.config.js'), 6 | eslint: require('./webpack-config/vendor/eslint.config.js'), 7 | postcss: require('./webpack-config/vendor/postcss.config.js'), 8 | }; 9 | -------------------------------------------------------------------------------- /core/components/footer/html.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /core/components/header/html.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <% if (pageTitle) { %> <%= pageTitle %> - <% } %> XXXX后台 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /core/components/side-menu/html.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/components/top-nav/html.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/config/bootstrap.config.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | module.exports = { 3 | styleLoader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss!less'), 4 | // styleLoader: 'file?name=bootstrap.min.css!raw!css?minimize&-autoprefixer!postcss!less', 5 | scripts: { 6 | transition: true, 7 | alert: true, 8 | button: true, 9 | carousel: true, 10 | collapse: true, 11 | dropdown: true, 12 | modal: true, 13 | tooltip: true, 14 | popover: true, 15 | scrollspy: true, 16 | tab: true, 17 | affix: true, 18 | }, 19 | styles: { 20 | mixins: true, 21 | 22 | normalize: true, 23 | print: true, 24 | 25 | scaffolding: true, 26 | type: true, 27 | code: true, 28 | grid: true, 29 | tables: true, 30 | forms: true, 31 | buttons: true, 32 | 33 | 'component-animations': true, 34 | glyphicons: false, 35 | dropdowns: true, 36 | 'button-groups': true, 37 | 'input-groups': true, 38 | navs: true, 39 | navbar: true, 40 | breadcrumbs: true, 41 | pagination: true, 42 | pager: true, 43 | labels: true, 44 | badges: true, 45 | jumbotron: true, 46 | thumbnails: true, 47 | alerts: true, 48 | 'progress-bars': true, 49 | media: true, 50 | 'list-group': true, 51 | panels: true, 52 | wells: true, 53 | close: true, 54 | 55 | modals: true, 56 | tooltip: true, 57 | popovers: true, 58 | carousel: true, 59 | 60 | utilities: true, 61 | 'responsive-utilities': true, 62 | }, 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /core/config/bootstrap.config.less: -------------------------------------------------------------------------------- 1 | // Modify bootstrap variables here. 2 | 3 | // a example: 4 | // @body-bg: @gray-lighter; 5 | 6 | -------------------------------------------------------------------------------- /core/config/build-file.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | js: { 3 | xdomain: require('!!file-loader?name=static/js/[name].[ext]!vendorDir/ie-fix/xdomain.all.js'), 4 | html5shiv: require('!!file-loader?name=static/js/[name].[ext]!vendorDir/ie-fix/html5shiv.min.js'), 5 | respond: require('!!file-loader?name=static/js/[name].[ext]!vendorDir/ie-fix/respond.min.js'), 6 | }, 7 | dll: { 8 | js: require('!!file-loader?name=dll/dll.js!dllDir/dll.js'), 9 | css: require('!file-loader?name=dll/dll.css!dllDir/dll.css'), 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /core/config/common.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /core/iconfont/demo.css: -------------------------------------------------------------------------------- 1 | *{margin: 0;padding: 0;list-style: none;} 2 | /* 3 | KISSY CSS Reset 4 | 理念:1. reset 的目的不是清除浏览器的默认样式,这仅是部分工作。清除和重置是紧密不可分的。 5 | 2. reset 的目的不是让默认样式在所有浏览器下一致,而是减少默认样式有可能带来的问题。 6 | 3. reset 期望提供一套普适通用的基础样式。但没有银弹,推荐根据具体需求,裁剪和修改后再使用。 7 | 特色:1. 适应中文;2. 基于最新主流浏览器。 8 | 维护:玉伯, 正淳 9 | */ 10 | 11 | /** 清除内外边距 **/ 12 | body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ 13 | dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ 14 | pre, /* text formatting elements 文本格式元素 */ 15 | form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */ 16 | th, td /* table elements 表格元素 */ { 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | /** 设置默认字体 **/ 22 | body, 23 | button, input, select, textarea /* for ie */ { 24 | font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif; 25 | } 26 | h1, h2, h3, h4, h5, h6 { font-size: 100%; } 27 | address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */ 28 | code, kbd, pre, samp { font-family: courier new, courier, monospace; } /* 统一等宽字体 */ 29 | small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */ 30 | 31 | /** 重置列表元素 **/ 32 | ul, ol { list-style: none; } 33 | 34 | /** 重置文本格式元素 **/ 35 | a { text-decoration: none; } 36 | a:hover { text-decoration: underline; } 37 | 38 | 39 | /** 重置表单元素 **/ 40 | legend { color: #000; } /* for ie6 */ 41 | fieldset, img { border: 0; } /* img 搭车:让链接里的 img 无边框 */ 42 | button, input, select, textarea { font-size: 100%; } /* 使得表单元素在 ie 下能继承字体大小 */ 43 | /* 注:optgroup 无法扶正 */ 44 | 45 | /** 重置表格元素 **/ 46 | table { border-collapse: collapse; border-spacing: 0; } 47 | 48 | /* 清除浮动 */ 49 | .ks-clear:after, .clear:after { 50 | content: '\20'; 51 | display: block; 52 | height: 0; 53 | clear: both; 54 | } 55 | .ks-clear, .clear { 56 | *zoom: 1; 57 | } 58 | 59 | .main {padding: 30px 100px;} 60 | .main h1{font-size:36px; color:#333; text-align:left;margin-bottom:30px; border-bottom: 1px solid #eee;} 61 | 62 | .helps{margin-top:40px;} 63 | .helps pre{ 64 | padding:20px; 65 | margin:10px 0; 66 | border:solid 1px #e7e1cd; 67 | background-color: #fffdef; 68 | overflow: auto; 69 | } 70 | 71 | .icon_lists li{ 72 | float:left; 73 | width: 100px; 74 | height:180px; 75 | text-align: center; 76 | } 77 | .icon_lists .icon{ 78 | font-size: 42px; 79 | line-height: 100px; 80 | margin: 10px 0; 81 | color:#333; 82 | -webkit-transition: font-size 0.25s ease-out 0s; 83 | -moz-transition: font-size 0.25s ease-out 0s; 84 | transition: font-size 0.25s ease-out 0s; 85 | 86 | } 87 | .icon_lists .icon:hover{ 88 | font-size: 100px; 89 | } 90 | -------------------------------------------------------------------------------- /core/iconfont/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | IconFont 7 | 8 | 9 | 10 | 11 |
12 |

IconFont 图标

13 |
    14 | 15 |
  • 16 | 17 |
    编辑
    18 |
    &#xe600;
    19 |
    .edit
    20 |
  • 21 | 22 |
  • 23 | 24 |
    提醒
    25 |
    &#xe601;
    26 |
    .alert
    27 |
  • 28 | 29 |
  • 30 | 31 |
    锁定
    32 |
    &#xe602;
    33 |
    .password
    34 |
  • 35 | 36 |
  • 37 | 38 |
    用户
    39 |
    &#xe603;
    40 |
    .mine
    41 |
  • 42 | 43 |
  • 44 | 45 |
    退出
    46 |
    &#xe604;
    47 |
    .logout
    48 |
  • 49 | 50 |
  • 51 | 52 |
    新窗口打开
    53 |
    &#xe606;
    54 |
    .demo-page
    55 |
  • 56 | 57 |
  • 58 | 59 |
    向下
    60 |
    &#xe607;
    61 |
    .arrow-down
    62 |
  • 63 | 64 |
  • 65 | 66 |
    三角
    67 |
    &#xe605;
    68 |
    .triangle-down
    69 |
  • 70 | 71 |
  • 72 | 73 |
    返回
    74 |
    &#xe608;
    75 |
    .arrow-left
    76 |
  • 77 | 78 |
79 | 80 | 81 |
82 | 第一步:使用font-face声明字体 83 |
 84 | @font-face {font-family: 'iconfont';
 85 |     src: url('iconfont.eot'); /* IE9*/
 86 |     src: url('iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
 87 |     url('iconfont.woff') format('woff'), /* chrome、firefox */
 88 |     url('iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
 89 |     url('iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
 90 | }
 91 | 
92 | 第二步:定义使用iconfont的样式 93 |
 94 | .iconfont{
 95 |     font-family:"iconfont" !important;
 96 |     font-size:16px;font-style:normal;
 97 |     -webkit-font-smoothing: antialiased;
 98 |     -webkit-text-stroke-width: 0.2px;
 99 |     -moz-osx-font-smoothing: grayscale;}
100 | 
101 | 第三步:挑选相应图标并获取字体编码,应用于页面 102 |
103 | <i class="iconfont">&#x33;</i>
104 | 
105 |
106 | 107 |
108 | 109 | 110 | -------------------------------------------------------------------------------- /core/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1473142795'); /* IE9*/ 4 | src: url('iconfont.eot?t=1473142795#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('iconfont.woff?t=1473142795') format('woff'), /* chrome, firefox */ 6 | url('iconfont.ttf?t=1473142795') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1473142795#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -webkit-text-stroke-width: 0.2px; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | .icon-edit:before { content: "\e600"; } 19 | .icon-alert:before { content: "\e601"; } 20 | .icon-password:before { content: "\e602"; } 21 | .icon-mine:before { content: "\e603"; } 22 | .icon-logout:before { content: "\e604"; } 23 | .icon-demo-page:before { content: "\e606"; } 24 | .icon-arrow-down:before { content: "\e607"; } 25 | .icon-triangle-down:before { content: "\e605"; } 26 | .icon-arrow-left:before { content: "\e608"; } 27 | -------------------------------------------------------------------------------- /core/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/core/iconfont/iconfont.eot -------------------------------------------------------------------------------- /core/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Tue Sep 6 14:19:55 2016 6 | By admin 7 | 8 | 9 | 10 | 24 | 26 | 28 | 30 | 32 | 36 | 38 | 42 | 45 | 50 | 54 | 56 | 60 | 62 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /core/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/core/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /core/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/core/iconfont/iconfont.woff -------------------------------------------------------------------------------- /core/layout/layout-without-nav/html.ejs: -------------------------------------------------------------------------------- 1 | <%= header %> 2 |
3 | <%= content %> 4 |
5 | <%= footer %> -------------------------------------------------------------------------------- /core/layout/layout-without-nav/html.js: -------------------------------------------------------------------------------- 1 | const config = require('configModule'); 2 | const noJquery = require('withoutJqueryModule'); 3 | const layout = require('./html.ejs'); 4 | const header = require('../../components/header/html.ejs'); 5 | const footer = require('../../components/footer/html.ejs'); 6 | const dirsConfig = config.DIRS; 7 | 8 | const pf = { 9 | pageTitle: '', 10 | constructInsideUrl: noJquery.constructInsideUrl, 11 | }; 12 | 13 | const moduleExports = { 14 | init({ pageTitle }) { 15 | pf.pageTitle = pageTitle; 16 | return this; 17 | }, 18 | run(content) { 19 | const headerRenderData = Object.assign(dirsConfig, pf); 20 | const renderData = { 21 | header: header(headerRenderData), 22 | footer: footer(), 23 | content, 24 | }; 25 | return layout(renderData); 26 | }, 27 | }; 28 | 29 | module.exports = moduleExports; 30 | -------------------------------------------------------------------------------- /core/layout/layout/html.ejs: -------------------------------------------------------------------------------- 1 | <%= header %> 2 |
3 | <%= topNav %> 4 | <%= sideMenu %> 5 | <%= content %> 6 |
7 | <%= footer %> -------------------------------------------------------------------------------- /core/layout/layout/html.js: -------------------------------------------------------------------------------- 1 | const config = require('configModule'); 2 | const noJquery = require('withoutJqueryModule'); 3 | const layout = require('./html.ejs'); // 整个页面布局的模板文件,主要是用来统筹各个公共组件的结构 4 | const header = require('../../components/header/html.ejs'); // 页头的模板 5 | const footer = require('../../components/footer/html.ejs'); // 页脚的模板 6 | const topNav = require('../../components/top-nav/html.ejs'); // 顶部栏的模板 7 | const sideMenu = require('../../components/side-menu/html.ejs'); // 侧边栏的模板 8 | const dirsConfig = config.DIRS; 9 | 10 | /* 整理渲染公共部分所用到的模板变量 */ 11 | const pf = { 12 | pageTitle: '', 13 | constructInsideUrl: noJquery.constructInsideUrl, 14 | }; 15 | 16 | const moduleExports = { 17 | /* 处理各个页面传入而又需要在公共区域用到的参数 */ 18 | init({ pageTitle }) { 19 | pf.pageTitle = pageTitle; // 比如说页面名称,会在或面包屑里用到 20 | return this; 21 | }, 22 | 23 | /* 整合各公共组件和页面实际内容,最后生成完整的HTML文档 */ 24 | run(content) { 25 | const headerRenderData = Object.assign(dirsConfig, pf); // 页头组件需要加载css/js等,因此需要比较多的变量 26 | const renderData = { 27 | header: header(headerRenderData), 28 | footer: footer(), 29 | topNav: topNav(pf), 30 | sideMenu: sideMenu(pf), 31 | content, 32 | }; 33 | return layout(renderData); 34 | }, 35 | }; 36 | 37 | module.exports = moduleExports; 38 | -------------------------------------------------------------------------------- /core/less/base-dir/_badge.less: -------------------------------------------------------------------------------- 1 | .badge-important { 2 | background-color: @brand-danger; 3 | } 4 | .badge-info { 5 | background-color: @brand-primary; 6 | } 7 | .badge-success { 8 | background-color: @brand-success; 9 | } 10 | .badge-warning { 11 | background-color: @brand-warning; 12 | } 13 | .badge-inverse { 14 | background-color: #333; 15 | } 16 | -------------------------------------------------------------------------------- /core/less/base-dir/_button.less: -------------------------------------------------------------------------------- 1 | .btn-outline { 2 | color: inherit; 3 | background-color: transparent; 4 | transition: all .5s; 5 | } 6 | 7 | .btn-primary.btn-outline { 8 | color: @brand-primary; 9 | } 10 | 11 | .btn-success.btn-outline { 12 | color: @brand-success; 13 | } 14 | 15 | .btn-info.btn-outline { 16 | color: @brand-info; 17 | } 18 | 19 | .btn-warning.btn-outline { 20 | color: @brand-warning; 21 | } 22 | 23 | .btn-danger.btn-outline { 24 | color: @brand-danger; 25 | } 26 | 27 | .btn-primary.btn-outline:hover, 28 | .btn-success.btn-outline:hover, 29 | .btn-info.btn-outline:hover, 30 | .btn-warning.btn-outline:hover, 31 | .btn-danger.btn-outline:hover { 32 | color: #fff; 33 | } 34 | 35 | /* 圆形按钮 */ 36 | .btn-circle { 37 | width: 30px; 38 | height: 30px; 39 | padding: 6px 0; 40 | border-radius: 15px; 41 | text-align: center; 42 | font-size: 12px; 43 | line-height: 1.428571429; 44 | 45 | &.btn-lg { 46 | width: 50px; 47 | height: 50px; 48 | padding: 10px 16px; 49 | border-radius: 25px; 50 | font-size: 18px; 51 | line-height: 1.33; 52 | } 53 | 54 | &.btn-xl { 55 | width: 70px; 56 | height: 70px; 57 | padding: 10px 16px; 58 | border-radius: 35px; 59 | font-size: 24px; 60 | line-height: 1.33; 61 | } 62 | } -------------------------------------------------------------------------------- /core/less/base-dir/_form.less: -------------------------------------------------------------------------------- 1 | @media(min-width:768px) { //PC端 2 | .form-inline { 3 | .form-group { 4 | margin-top: 2px; 5 | margin-bottom: 2px; 6 | margin-right: 15px; 7 | white-space: nowrap; 8 | } 9 | } 10 | } 11 | 12 | .form-inline { 13 | & > .btn, & > label { 14 | margin-left: 10px; 15 | margin-right: 10px; 16 | } 17 | } 18 | 19 | .form-group .checkbox input[type="checkbox"] { 20 | margin-left: 0; 21 | } 22 | 23 | .form-required > label:after { 24 | content: " * "; 25 | color: red; 26 | } -------------------------------------------------------------------------------- /core/less/base-dir/_global.less: -------------------------------------------------------------------------------- 1 | /* 全局通用style */ 2 | * { 3 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; 4 | } 5 | 6 | html, body { 7 | .full-screen(); 8 | background-color: @gray-lightest; 9 | } 10 | 11 | a { 12 | .cursorPointer(); 13 | } 14 | 15 | /* 总容器 */ 16 | #wrapper { 17 | .full-screen(); 18 | } 19 | 20 | #page-wrapper { 21 | padding: 5px 5px 5px 5px; 22 | background-color: white; 23 | 24 | .page-header { 25 | font-size: 20px; 26 | } 27 | } 28 | @media(min-width:768px) { //PC端 29 | #page-wrapper { 30 | position: absolute; 31 | top: @top-bar-height + @top-bar-boder-width; 32 | bottom: 0; 33 | left: 250px; 34 | right: 0; 35 | padding: 10px 10px 10px 10px; 36 | overflow-y: auto; 37 | border-left: 1px solid darken(@gray-lightest, 6.5%); 38 | } 39 | } 40 | 41 | .picvcode { 42 | min-width: 80px; 43 | } 44 | 45 | /* 隐藏首屏加载页面时出现的花括号 */ 46 | .ms-controller{ 47 | visibility: hidden 48 | } -------------------------------------------------------------------------------- /core/less/base-dir/_iconfont.less: -------------------------------------------------------------------------------- 1 | /* 搬fontawesome的样式过来用 */ 2 | .iconfont { 3 | display: inline-block; 4 | width: 1.5em; 5 | text-align: center; 6 | font-size: inherit; 7 | text-rendering: auto; 8 | } 9 | 10 | /* 图标无限旋转 */ 11 | .iconfont--spin { 12 | -webkit-animation: iconfont--spin 2s infinite linear; 13 | animation: iconfont--spin 2s infinite linear; 14 | } 15 | @-webkit-keyframes iconfont--spin { 16 | 0% { 17 | -webkit-transform: rotate(0deg); 18 | transform: rotate(0deg); 19 | } 20 | 100% { 21 | -webkit-transform: rotate(359deg); 22 | transform: rotate(359deg); 23 | } 24 | } 25 | @keyframes iconfont--spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } -------------------------------------------------------------------------------- /core/less/base-dir/_mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | .full-screen() { 3 | width: 100%; 4 | } 5 | 6 | .size(@width, @height) { 7 | width: @width; 8 | height: @height; 9 | } 10 | 11 | .fl() { 12 | float: left; 13 | } 14 | 15 | .fr() { 16 | float: right; 17 | } 18 | 19 | .clearfix() { 20 | &:before,&:after{ 21 | content:""; 22 | display:table; 23 | } 24 | &:after{clear:both;} 25 | &{ 26 | *zoom:1;/*IE/7/6*/ 27 | } 28 | } 29 | 30 | .noselect() { 31 | -moz-user-select:none;/*火狐*/ 32 | -webkit-user-select:none;/*webkit浏览器*/ 33 | -ms-user-select:none;/*IE10*/ 34 | -khtml-user-select:none;/*早期浏览器*/ 35 | user-select:none; 36 | } 37 | 38 | .block-center() { 39 | margin-right: auto; 40 | margin-left: auto; 41 | display: block; 42 | } 43 | 44 | .text-ellipsis() { 45 | white-space: nowrap; 46 | overflow: hidden; 47 | text-overflow: ellipsis; 48 | } 49 | 50 | .cursorPointer() { 51 | cursor: pointer; 52 | } 53 | .cursorPointer { 54 | .cursorPointer(); 55 | } 56 | 57 | .padding0() { 58 | padding: 0; 59 | } 60 | .padding0 { 61 | .padding0(); 62 | } -------------------------------------------------------------------------------- /core/less/base-dir/_panel.less: -------------------------------------------------------------------------------- 1 | /* 彩色panel */ 2 | .panel-green { 3 | border-color: @brand-success; 4 | .panel-heading { 5 | border-color: @brand-success; 6 | color: white; 7 | background-color: @brand-success; 8 | } 9 | a { 10 | color: @brand-success; 11 | &:hover { 12 | color: darken(@brand-success, 15%); 13 | } 14 | } 15 | } 16 | 17 | .panel-red { 18 | border-color: @brand-danger; 19 | .panel-heading { 20 | border-color: @brand-danger; 21 | color: white; 22 | background-color: @brand-danger; 23 | } 24 | a { 25 | color: @brand-danger; 26 | &:hover { 27 | color: darken(@brand-danger, 15%); 28 | } 29 | } 30 | } 31 | 32 | .panel-yellow { 33 | border-color: @brand-warning; 34 | .panel-heading { 35 | border-color: @brand-warning; 36 | color: white; 37 | background-color: @brand-warning; 38 | } 39 | a { 40 | color: @brand-warning; 41 | &:hover { 42 | color: darken(@brand-warning, 15%); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /core/less/base-dir/_table.less: -------------------------------------------------------------------------------- 1 | @media(min-width:768px) { //PC端 2 | .table td .btn { 3 | margin-top: 2px; 4 | margin-bottom: 2px; 5 | } 6 | } 7 | 8 | .table td .btn { 9 | margin-left: 2px; 10 | margin-right: 2px; 11 | } -------------------------------------------------------------------------------- /core/less/base-dir/_variables.less: -------------------------------------------------------------------------------- 1 | /* 颜色 */ 2 | @gray-darker: lighten(#000, 13.5%); 3 | @gray-dark: lighten(#000, 20%); 4 | @gray: lighten(#000, 33.5%); 5 | @gray-light: lighten(#000, 60%); 6 | @gray-lighter: lighten(#000, 93.5%); 7 | @gray-lightest: lighten(#000, 97.25%); 8 | @brand-primary: #428bca; 9 | @brand-success: #5cb85c; 10 | @brand-info: #5bc0de; 11 | @brand-warning: #f0ad4e; 12 | @brand-danger: #d9534f; 13 | 14 | /* 各种尺寸 */ 15 | @top-bar-height: 51px; 16 | @top-bar-boder-width: 1px; -------------------------------------------------------------------------------- /core/less/base.less: -------------------------------------------------------------------------------- 1 | @base-dir: './base-dir'; 2 | @widgets-dir: './widgets-dir'; 3 | @components-dir: './components-dir'; 4 | 5 | @import "@{base-dir}/_variables.less"; 6 | @import "@{base-dir}/_mixins.less"; 7 | 8 | /* 全局样式 */ 9 | @import "@{base-dir}/_global.less"; 10 | 11 | /* iconfont相关 */ 12 | @import "@{base-dir}/_iconfont.less"; 13 | 14 | /* 按钮 */ 15 | @import "@{base-dir}/_button.less"; 16 | 17 | /* panel(面板) */ 18 | @import "@{base-dir}/_panel.less"; 19 | 20 | /* badge(徽章) */ 21 | @import "@{base-dir}/_badge.less"; 22 | 23 | /* 表单 */ 24 | @import "@{base-dir}/_form.less"; 25 | 26 | /* 表格 */ 27 | @import "@{base-dir}/_table.less"; 28 | 29 | /* 顶部菜单栏 */ 30 | @import "@{components-dir}/_top-menu.less"; 31 | 32 | /* 侧边导航栏 */ 33 | @import "@{components-dir}/_side-nav.less"; 34 | 35 | /* 模态弹出框 */ 36 | @import "@{components-dir}/_modal.less"; 37 | 38 | /* 标签页 */ 39 | @import "@{components-dir}/_tab.less"; 40 | -------------------------------------------------------------------------------- /core/less/components-dir/_modal.less: -------------------------------------------------------------------------------- 1 | @media (min-width: 1280px) { 2 | .modal-lg { 3 | width: 1200px; 4 | } 5 | } 6 | 7 | @media (min-width: 1440px) { 8 | .modal-lg { 9 | width: 1350px; 10 | } 11 | } -------------------------------------------------------------------------------- /core/less/components-dir/_side-nav.less: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | #side-menu { 3 | visibility: hidden; 4 | } 5 | 6 | ul li { 7 | border-bottom: 1px solid darken(@gray-lightest, 6.5%); 8 | a { 9 | &.active { 10 | background-color: @gray-lighter; 11 | } 12 | } 13 | } 14 | 15 | .sidebar-nav.navbar-collapse { 16 | padding-left: 0; 17 | padding-right: 0; 18 | } 19 | 20 | .sidebar-search { 21 | padding: 15px; 22 | } 23 | 24 | .arrow { 25 | float: right; 26 | 27 | &.iconfont:before { 28 | content: "\e608"; 29 | } 30 | } 31 | 32 | .active > a > .iconfont.arrow:before { 33 | content: "\e607"; 34 | } 35 | 36 | .nav-second-level li, .nav-third-level li { 37 | border-bottom: none !important; 38 | } 39 | 40 | .nav-second-level li a { 41 | padding-left: 37px; 42 | } 43 | 44 | .nav-third-level li a { 45 | padding-left: 52px; 46 | } 47 | } 48 | 49 | @media(min-width:768px) { //PC端 50 | .sidebar { 51 | z-index: 1; 52 | position: absolute; 53 | width: 250px; 54 | top: @top-bar-height + @top-bar-boder-width; 55 | bottom: 0; 56 | overflow-y: auto; 57 | } 58 | 59 | .navbar-top-links .dropdown-messages, 60 | .navbar-top-links .dropdown-tasks, 61 | .navbar-top-links .dropdown-alerts { 62 | margin-left: auto; 63 | } 64 | } -------------------------------------------------------------------------------- /core/less/components-dir/_tab.less: -------------------------------------------------------------------------------- 1 | .tab-content { 2 | margin-top: 10px; 3 | } 4 | -------------------------------------------------------------------------------- /core/less/components-dir/_top-menu.less: -------------------------------------------------------------------------------- 1 | .navbar { 2 | margin-bottom: 0; 3 | } 4 | 5 | .navbar-top-links { 6 | margin-right: 15px; 7 | 8 | li { 9 | display: inline-block; 10 | 11 | a { 12 | padding: 15px; 13 | // min-height: 50px; 14 | } 15 | } 16 | 17 | .dropdown-menu li { 18 | display: block; 19 | 20 | a { 21 | padding: 3px 20px; 22 | // min-height: 0; 23 | 24 | div { 25 | white-space: normal; 26 | } 27 | } 28 | } 29 | 30 | .dropdown-messages, .dropdown-tasks, .dropdown-alerts { 31 | width: 310px; 32 | min-width: 0; 33 | } 34 | 35 | .dropdown-messages { 36 | margin-left: 5px; 37 | } 38 | 39 | .dropdown-tasks { 40 | margin-left: -59px; 41 | } 42 | 43 | .dropdown-alerts { 44 | margin-left: -123px; 45 | } 46 | 47 | .dropdown-user { 48 | right: 0; 49 | left: auto; 50 | } 51 | } 52 | @media (min-width: 768px) { 53 | .navbar-right { 54 | margin-right: 15px; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/libs/libs.module.js: -------------------------------------------------------------------------------- 1 | const moduleExports = { 2 | /** 3 | * URL相关 开始 4 | */ 5 | /* 在新窗口打开指定的URL */ 6 | openNewWindow(redirectUrl) { 7 | $('body').append(`<a href="${redirectUrl}" target="_blank" id="hidden-link" style="visibility: hidden;">在新窗口打开指定URL</a>`); 8 | document.getElementById('hidden-link').click(); 9 | }, 10 | /* URL相关 结束 */ 11 | 12 | /* 从一个对象中取出指定的字段形成一个新对象 */ 13 | fetchObjFields(fieldsArr, obj) { 14 | const resultObj = {}; 15 | for (let i = 0; i < fieldsArr.length; i ++) { 16 | const _key = fieldsArr[i]; 17 | resultObj[_key] = obj[_key]; 18 | } 19 | return resultObj; 20 | }, 21 | 22 | /* 在页面间传递参数 */ 23 | sendParamBetweenPages(toPage, key, value) { 24 | const localStorageKey = `${key}-to-${toPage}Page`; 25 | this.locStorage.set(localStorageKey, value); 26 | }, 27 | /* 在页面间接受参数 */ 28 | receiveParamBetweenPages(targetPage, key, dontRemove = false) { 29 | const localStorageKey = `${key}-to-${targetPage}Page`; 30 | const val = this.locStorage.get(localStorageKey); 31 | 32 | if (!dontRemove) { 33 | this.locStorage.remove(localStorageKey); 34 | } 35 | 36 | return val; 37 | }, 38 | _eventListenerArr: {}, 39 | /** 40 | * 监听自定义事件 41 | * 示例: 42 | libs.addEventListener({ 43 | eventName: 'order-status-select', 44 | cb: (ret) => { 45 | if (ret) { 46 | console.log(ret.value); 47 | } 48 | }, 49 | ); 50 | * 51 | */ 52 | addEventListener({ eventName, targetPage, cb }) { 53 | const config = require('configModule'); 54 | const intervalId = window.setInterval(() => { 55 | let dontRemove = false; 56 | if (!targetPage) { 57 | targetPage = 'any-page'; 58 | dontRemove = true; 59 | } 60 | const ret = this.receiveParamBetweenPages(targetPage, `event-${eventName}`, dontRemove); 61 | if (!!ret && typeof ret === 'object') { 62 | if ($.isFunction(cb)) { 63 | cb(ret); 64 | } 65 | } 66 | }, config.CHECK_CROSS_PAGE_EVENT_PER_TIME); 67 | this._eventListenerArr[eventName] = intervalId; 68 | }, 69 | /** 70 | * 移除事件监听 71 | * 示例: 72 | libs.removeEventListener({ eventName: 'order-status-select' }); 73 | * 74 | */ 75 | removeEventListener({ eventName }) { 76 | clearInterval(this._eventListenerArr[eventName]); 77 | }, 78 | 79 | /** 80 | * 将任意一个自定义事件广播出去,该事件可在任意页面通过addEventListener监听收到。 81 | * 示例: 82 | libs.sendEvent({ eventName: 'user-alerdy-login' }); 83 | libs.sendEvent({ 84 | eventName: 'user-alerdy-login', //事件名 85 | extra: { // callback中可取到的参数 86 | key1: 'value1', 87 | key2: 'value2' 88 | } 89 | }); 90 | libs.sendEvent({ 91 | eventName: 'user-alerdy-login', 92 | extra: { 93 | key1: 'value1', 94 | key2: 'value2', 95 | }, 96 | }); 97 | * 98 | */ 99 | sendEvent({ eventName, targetPage, extra }) { 100 | const ret = { 101 | value: extra, 102 | }; 103 | targetPage = !!targetPage ? targetPage : 'any-page'; 104 | this.sendParamBetweenPages(targetPage, `event-${eventName}`, ret); 105 | }, 106 | 107 | /* 格式化处理浮点型(带小数位) */ 108 | formatFloat(num, pos) { 109 | pos = pos || 2; 110 | return Math.round(num * Math.pow(10, pos)) / Math.pow(10, pos); 111 | }, 112 | 113 | /* 获取URL上的get参数 */ 114 | getRequestParams() { 115 | const url = window.location.search; // 获取url中"?"符后的字串 116 | const theRequest = {}; 117 | if (url.indexOf('?') !== -1) { 118 | const str = url.substr(1); 119 | const strs = str.split('&'); 120 | for (let i = 0; i < strs.length; i ++) { 121 | theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1]); 122 | } 123 | } 124 | return theRequest; 125 | }, 126 | 127 | tooltip: { 128 | init() { 129 | $('[data-toggle="tooltip"]').tooltip({ 130 | container: 'body', 131 | placement: 'top', 132 | }); 133 | }, 134 | 135 | hide($dom) { 136 | $dom.tooltip('hide'); 137 | }, 138 | }, 139 | }; 140 | 141 | module.exports = moduleExports; 142 | -------------------------------------------------------------------------------- /core/libs/without-jquery.module.js: -------------------------------------------------------------------------------- 1 | const config = require('configModule'); 2 | module.exports = { 3 | /* 拼接系统内部的URL */ 4 | constructInsideUrl(url, urlTail) { 5 | urlTail = urlTail || ''; 6 | return config.PAGE_ROOT_PATH + url + '/page.html' + urlTail; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /core/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dll", 3 | "content": { 4 | "./node_modules/jquery/dist/jquery.js": 71, 5 | "./node_modules/expose-loader/index.js?jQuery!./node_modules/jquery/dist/jquery.js": 70, 6 | "./node_modules/bootstrap-webpack/index.loader.js!./core/config/bootstrap.config.js": 72, 7 | "./node_modules/extract-text-webpack-plugin/loader.js?{\"remove\":true}!./node_modules/css-loader/index.js?minimize&-autoprefixer!./node_modules/postcss-loader/index.js!./node_modules/less-loader/index.js!./node_modules/bootstrap-webpack/bootstrap-styles.loader.js!./core/config/bootstrap.config.js": 73, 8 | "./node_modules/bootstrap-webpack/bootstrap-scripts.loader.js!./core/config/bootstrap.config.js": 75, 9 | "./node_modules/bootstrap/js/transition.js": 76, 10 | "./node_modules/bootstrap/js/alert.js": 77, 11 | "./node_modules/bootstrap/js/button.js": 78, 12 | "./node_modules/bootstrap/js/carousel.js": 79, 13 | "./node_modules/bootstrap/js/collapse.js": 80, 14 | "./node_modules/bootstrap/js/dropdown.js": 81, 15 | "./node_modules/bootstrap/js/modal.js": 82, 16 | "./node_modules/bootstrap/js/tooltip.js": 83, 17 | "./node_modules/bootstrap/js/popover.js": 84, 18 | "./node_modules/bootstrap/js/scrollspy.js": 85, 19 | "./node_modules/bootstrap/js/tab.js": 86, 20 | "./node_modules/bootstrap/js/affix.js": 87, 21 | "./vendor/metisMenu/metisMenu.min.js": 88, 22 | "./vendor/metisMenu/metisMenu.min.css": 89 23 | } 24 | } -------------------------------------------------------------------------------- /core/npm-scripts/dll.script.js: -------------------------------------------------------------------------------- 1 | const clean = require('../../npm-scripts/clean-dirs.script.js'); 2 | const dirVars = require('../webpack-config/base/dir-vars.config.js'); 3 | clean([dirVars.dllDir]); 4 | -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "dll": "node npm-scripts/dll.script.js && webpack --progress --colors --config ./webpack-dll.config.js" 7 | }, 8 | "author": "", 9 | "license": "ISC" 10 | } 11 | -------------------------------------------------------------------------------- /core/webpack-config/base/dir-vars.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var mE = {}; 3 | 4 | // 源文件目录 5 | mE.staticRootDir = path.resolve(__dirname, '../../../'); // 根目录 6 | /**/mE.vendorDir = path.resolve(mE.staticRootDir, './vendor'); // 存放所有不能用npm管理的第三方库 7 | /**/mE.coreDir = path.resolve(mE.staticRootDir, './core'); // 项目框架 8 | /**//**/mE.dllDir = path.resolve(mE.coreDir, './dll'); // 存放由各种不常改变的js/css打包而来的dll 9 | /**//**/mE.libsDir = path.resolve(mE.coreDir, './libs'); // 与业务逻辑无关的库都可以放到这里 10 | /**//**/mE.coreConfigDir = path.resolve(mE.coreDir, './config'); // 存放项目框架的各种配置文件 11 | /**//**/mE.componentsDir = path.resolve(mE.coreDir, './components'); // 存放组件,可以是纯HTML,也可以包含js/css/image等,看自己需要 12 | /**//**/mE.layoutDir = path.resolve(mE.coreDir, './layout'); // 存放UI布局,组织各个组件拼起来,因应需要可以有不同的布局套路 13 | 14 | 15 | module.exports = mE; 16 | -------------------------------------------------------------------------------- /core/webpack-config/inherit/plugins.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 3 | var dirVars = require('../base/dir-vars.config.js'); 4 | 5 | var configPlugins = [ 6 | /* 全局shimming */ 7 | new webpack.ProvidePlugin({ 8 | $: 'jquery', 9 | jQuery: 'jquery', 10 | 'window.jQuery': 'jquery', 11 | 'window.$': 'jquery', 12 | }), 13 | /* 抽取出所有通用的部分 */ 14 | new webpack.optimize.CommonsChunkPlugin({ 15 | name: 'commons/commons', // 需要注意的是,chunk的name不能相同!!! 16 | filename: '[name]/bundle.js', 17 | minChunks: 4, 18 | }), 19 | /* 抽取出chunk的css */ 20 | new ExtractTextPlugin('[name]/styles.css'), 21 | /* 配置好Dll */ 22 | new webpack.DllReferencePlugin({ 23 | context: dirVars.staticRootDir, // 指定一个路径作为上下文环境,需要与DllPlugin的context参数保持一致,建议统一设置为项目根目录 24 | manifest: require('../../manifest.json'), // 指定manifest.json 25 | name: 'dll', // 当前Dll的所有内容都会存放在这个参数指定变量名的一个全局变量下,注意与DllPlugin的name参数保持一致 26 | }), 27 | ]; 28 | 29 | module.exports = configPlugins; 30 | -------------------------------------------------------------------------------- /core/webpack-config/module.config.js: -------------------------------------------------------------------------------- 1 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | const dirVars = require('./base/dir-vars.config.js'); 3 | const includeDirs = [dirVars.coreDir]; 4 | module.exports = { 5 | preLoaders: [{ 6 | test: /\.js$/, 7 | loader: 'eslint', 8 | include: includeDirs, 9 | exclude: [/bootstrap/], 10 | }], 11 | 12 | loaders: [ 13 | { 14 | test: require.resolve('jquery'), 15 | loader: 'expose?$!expose?jQuery', 16 | }, 17 | { 18 | test: /\.css$/, 19 | include: [dirVars.coreDir, dirVars.vendorDir], 20 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss'), 21 | }, 22 | { 23 | test: /\.less$/, 24 | include: includeDirs, 25 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss!less'), 26 | }, 27 | { 28 | test: /\.js$/, 29 | include: includeDirs, 30 | loader: 'babel-loader', 31 | query: { 32 | presets: ['es2015-loose'], 33 | cacheDirectory: true, 34 | plugins: ['transform-runtime', 'transform-es3-member-expression-literals', 'transform-es3-property-literals'], 35 | }, 36 | }, 37 | { 38 | test: /\.html$/, 39 | include: includeDirs, 40 | loader: 'html', 41 | }, 42 | { 43 | test: /\.ejs$/, 44 | include: includeDirs, 45 | loader: 'ejs', 46 | }, 47 | { 48 | // 图片加载器,雷同file-loader,更适合图片,可以将较小的图片转成base64,减少http请求 49 | // 如下配置,将小于8192byte的图片转成base64码 50 | test: /\.(png|jpg|gif)$/, 51 | include: includeDirs, 52 | loader: 'url?limit=8192&name=./static/img/[hash].[ext]', 53 | }, 54 | { 55 | // 专供iconfont方案使用的,后面会带一串时间戳,需要特别匹配到 56 | test: /\.(woff|woff2|svg|eot|ttf)\??.*$/, 57 | include: includeDirs, 58 | loader: 'file?name=./static/fonts/[name].[ext]', 59 | }, 60 | ], 61 | }; 62 | -------------------------------------------------------------------------------- /core/webpack-config/plugins.dev.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var pluginsConfig = require('./inherit/plugins.config.js'); 3 | 4 | pluginsConfig.push(new webpack.DefinePlugin({ 5 | IS_PRODUCTION: false, 6 | })); 7 | 8 | module.exports = pluginsConfig; 9 | -------------------------------------------------------------------------------- /core/webpack-config/plugins.product.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var pluginsConfig = require('./inherit/plugins.config.js'); 3 | 4 | /* webpack1下,用了压缩插件会导致所有loader添加min配置,而autoprefixser也被定格到某个browers配置 */ 5 | // pluginsConfig.push(new webpack.optimize.UglifyJsPlugin({ 6 | // compress: { 7 | // warnings: false, 8 | // }, 9 | // })); 10 | 11 | pluginsConfig.push(new webpack.DefinePlugin({ 12 | IS_PRODUCTION: true, 13 | })); 14 | 15 | pluginsConfig.push(new webpack.NoErrorsPlugin()); // 配合CLI的--bail,一出error就终止webpack的编译进程 16 | 17 | module.exports = pluginsConfig; 18 | -------------------------------------------------------------------------------- /core/webpack-config/resolve.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('./base/dir-vars.config.js'); 3 | module.exports = { 4 | // 模块别名的配置,为了使用方便,一般来说所有模块都是要配置一下别名的 5 | alias: { 6 | /* 各种目录 */ 7 | vendorDir: dirVars.vendorDir, 8 | coreConfigDir: dirVars.coreConfigDir, 9 | dllDir: dirVars.dllDir, 10 | lessDir: path.resolve(dirVars.coreDir, 'less'), 11 | iconfontDir: path.resolve(dirVars.coreDir, 'iconfont'), 12 | 13 | /* vendor */ 14 | /* bootstrap 相关 */ 15 | metisMenu: path.resolve(dirVars.vendorDir, 'metisMenu/'), 16 | 17 | /* libs */ 18 | withoutJqueryModule: path.resolve(dirVars.libsDir, 'without-jquery.module'), 19 | routerModule: path.resolve(dirVars.libsDir, 'router.module'), 20 | libs: path.resolve(dirVars.libsDir, 'libs.module'), 21 | 22 | /* components */ 23 | 24 | /* layout */ 25 | layout: path.resolve(dirVars.layoutDir, 'layout/html'), 26 | 'layout-without-nav': path.resolve(dirVars.layoutDir, 'layout-without-nav/html'), 27 | 28 | /* config */ 29 | bootstrapConfig: path.resolve(dirVars.coreConfigDir, 'bootstrap.config'), 30 | }, 31 | 32 | // 当require的模块找不到时,尝试添加这些后缀后进行寻找 33 | extentions: ['', 'js'], 34 | }; 35 | -------------------------------------------------------------------------------- /core/webpack-config/vendor/eslint.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('../base/dir-vars.config.js'); 3 | module.exports = { 4 | configFile: path.resolve(dirVars.staticRootDir, './.eslintrc'), 5 | failOnWarning: true, 6 | failOnError: true, 7 | cache: true, 8 | }; 9 | -------------------------------------------------------------------------------- /core/webpack-config/vendor/postcss.config.js: -------------------------------------------------------------------------------- 1 | var precss = require('precss'); 2 | var autoprefixer = require('autoprefixer'); 3 | module.exports = function postcss() { 4 | return [precss, autoprefixer({ 5 | remove: false, 6 | browsers: ['ie >= 8', '> 1% in CN'], 7 | })]; 8 | }; 9 | -------------------------------------------------------------------------------- /core/webpack-dll.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 3 | const dirVars = require('./webpack-config/base/dir-vars.config.js'); // 与业务代码共用同一份路径的配置表 4 | 5 | module.exports = { 6 | output: { 7 | path: dirVars.dllDir, 8 | filename: '[name].js', 9 | library: '[name]', // 当前Dll的所有内容都会存放在这个参数指定变量名的一个全局变量下,注意与DllPlugin的name参数保持一致 10 | }, 11 | entry: { 12 | /* 13 | 指定需要打包的js模块 14 | 或是css/less/图片/字体文件等资源,但注意要在module参数配置好相应的loader 15 | */ 16 | dll: [ 17 | 'jquery', '!!bootstrap-webpack!bootstrapConfig', 18 | 'metisMenu/metisMenu.min', 'metisMenu/metisMenu.min.css', 19 | ], 20 | }, 21 | plugins: [ 22 | new webpack.DllPlugin({ 23 | path: 'manifest.json', // 本Dll文件中各模块的索引,供DllReferencePlugin读取使用 24 | name: '[name]', // 当前Dll的所有内容都会存放在这个参数指定变量名的一个全局变量下,注意与参数output.library保持一致 25 | context: dirVars.staticRootDir, // 指定一个路径作为上下文环境,需要与DllReferencePlugin的context参数保持一致,建议统一设置为项目根目录 26 | }), 27 | /* 跟业务代码一样,该兼容的还是得兼容 */ 28 | new webpack.ProvidePlugin({ 29 | $: 'jquery', 30 | jQuery: 'jquery', 31 | 'window.jQuery': 'jquery', 32 | 'window.$': 'jquery', 33 | }), 34 | new ExtractTextPlugin('[name].css'), // 打包css/less的时候会用到ExtractTextPlugin 35 | // new webpack.optimize.UglifyJsPlugin({ 36 | // sourceMap: false, 37 | // mangle: false, 38 | // compress: { 39 | // warnings: false, 40 | // }, 41 | // }), 42 | ], 43 | module: require('./webpack-config/module.config.js'), // 沿用业务代码的module配置 44 | resolve: require('./webpack-config/resolve.config.js'), // 沿用业务代码的resolve配置 45 | }; 46 | -------------------------------------------------------------------------------- /debug.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/debug.log -------------------------------------------------------------------------------- /example-admin-1/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <script type="text/javascript"> 5 | window.location.href = 'index/index/page.html'; 6 | </script> 7 | </head> 8 | <body> 9 | 10 | </body> 11 | </html> -------------------------------------------------------------------------------- /example-admin-1/npm-scripts/build.script.js: -------------------------------------------------------------------------------- 1 | const clean = require('../../npm-scripts/clean-dirs.script.js'); 2 | const dirVars = require('../webpack-config/base/dir-vars.config.js'); 3 | clean([dirVars.buildDir]); 4 | -------------------------------------------------------------------------------- /example-admin-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-admin-1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node npm-scripts/build.script.js && webpack --progress --colors --bail --config ./webpack.product.config.js", 8 | "dev": "node npm-scripts/build.script.js && webpack --progress --colors --config ./webpack.dev.config.js", 9 | "watch": "node npm-scripts/build.script.js && webpack --progress --colors --watch --config ./webpack.dev.config.js", 10 | "start": "webpack-dev-server --inline --progress --compress --devtool eval --content-base build/ --config ./webpack.dev.config.js", 11 | "profile": "node npm-scripts/build.script.js && webpack --colors --profile --display-modules" 12 | }, 13 | "author": "", 14 | "license": "ISC" 15 | } 16 | -------------------------------------------------------------------------------- /example-admin-1/pages/alert/index/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div class="container-fluid" > 3 | <h2 class="page-header"><%= pageTitle %></h2> 4 | 5 | <div class="row"> 6 | <div class="col-md-12"> 7 | <div class="panel panel-default" id="filter-panel"> 8 | <div class="panel-heading"> 9 | 消息通知筛选条件 10 | </div> 11 | <!-- /.panel-heading --> 12 | <div class="panel-body"> 13 | <form class="form-inline"> 14 | <div class="form-group"> 15 | <label for="status-select">阅读状态:</label> 16 | <select class="form-control" id="status-select"> 17 | <option value="0">全部</option> 18 | <option value="1">已读</option> 19 | <option value="2">未读</option> 20 | </select> 21 | </div> 22 | <button type="button" class="btn btn-primary">确认筛选</button> 23 | </form> 24 | </div> 25 | <!-- /.panel-body --> 26 | </div> 27 | </div> 28 | </div> 29 | 30 | <!-- /.row --> 31 | <div class="row"> 32 | <div class="col-md-12"> 33 | <div class="panel panel-default" id="main-panel"> 34 | <div class="panel-heading"> 35 | 消息通知列表 36 | </div> 37 | <div class="panel-body"> 38 | <div class="table-container table-responsive"> 39 | <table class="table table-hover "> 40 | <thead> 41 | <tr> 42 | <th> 43 | <input type="checkbox"> 44 | </th> 45 | <th>通知时间</th> 46 | <th>内容</th> 47 | </tr> 48 | </thead> 49 | <tbody> 50 | <tr> 51 | <td> 52 | <input type="checkbox" value="543"> 53 | </td> 54 | <td>2016-08-01 08:53</td> 55 | <td>抱歉,您未通过平台审核!原因:2333</td> 56 | </tr> 57 | <tr> 58 | <td> 59 | <input type="checkbox" value="542"> 60 | </td> 61 | <td>2016-08-01 08:52</td> 62 | <td>系统已赠送了抵用券到您的账户,请在指定规则条件内使用。</td> 63 | </tr> 64 | </tbody> 65 | </table> 66 | </div> 67 | <div class="text-center"> 68 | <ul class="paginator-container"></ul> 69 | </div> 70 | </div> 71 | <div class="panel-footer text-center"> 72 | <button type="button" class="btn btn-success">设置为已读</button> 73 | </div> 74 | </div> 75 | </div> 76 | </div> 77 | </div> 78 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/alert/index/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); // 调取存放本页面实际内容的模板文件 2 | const layout = require('layout'); // 调用管理后台内部所使用的布局方案,我在webpack配置里定义其别名为'layout' 3 | const pageTitle = '消息通知'; // 页面名称 4 | 5 | // 给layout传入“页面名称”这一参数(当然有需要的话也可以传入其它参数),同时也传入页面实际内容的HTML字符串。content({ pageTitle })的意思就是把pageTitle作为模板变量传给ejs模板引擎并返回最终生成的HTML字符串。 6 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 7 | -------------------------------------------------------------------------------- /example-admin-1/pages/alert/index/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | $(() => { 3 | 4 | }); 5 | -------------------------------------------------------------------------------- /example-admin-1/pages/index/index/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div class="container-fluid"> 3 | <h2 class="page-header">欢迎进入XXX管理后台!</h2> 4 | </div> 5 | <!-- /.container-fluid --> 6 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/index/index/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | 4 | module.exports = layout.init({ 5 | pageTitle: '', 6 | }).run(content()); 7 | -------------------------------------------------------------------------------- /example-admin-1/pages/index/index/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | const config = require('configModule'); 3 | 4 | $(() => { 5 | /* global IS_PRODUCTION:true */ // 由于ESLint会检测没有定义的变量,因此需要这一个`global`注释声明IS_PRODUCTION是一个全局变量(当然在本例中并不是)来规避warning 6 | if (!IS_PRODUCTION) { 7 | console.log('如果你看到这个Log,那么这个版本实际上是开发用的版本'); 8 | console.log(config.API_ROOT); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/html.js: -------------------------------------------------------------------------------- 1 | const config = require('configModule'); 2 | const noJquery = require('withoutJqueryModule'); 3 | const content = require('./templates/main.ejs'); 4 | const layout = require('layout-without-nav'); 5 | const dirsConfig = config.DIRS; 6 | 7 | const loginBoxHtml = require('./templates/login-box.ejs')({ 8 | constructInsideUrl: noJquery.constructInsideUrl, 9 | }); 10 | const forgetPasswordHtml = require('./templates/forget-password-box.html'); 11 | const renderData = Object.assign(dirsConfig, { loginBoxHtml, forgetPasswordHtml }); 12 | 13 | module.exports = layout.init({ 14 | pageTitle: '', 15 | }).run(content(renderData)); 16 | -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/page.js: -------------------------------------------------------------------------------- 1 | require('!!bootstrap-webpack!bootstrapConfig'); 2 | require('lessDir/base.less'); 3 | require('./page.less'); 4 | 5 | window.switchToPage = (page) => { 6 | switch (page) { 7 | case 'login': 8 | $('#user-edit-password').hide(); 9 | $('#login-box').show(); 10 | break; 11 | 12 | case 'forget-password': 13 | $('#login-box').hide(); 14 | $('#user-edit-password').show(); 15 | break; 16 | 17 | default: 18 | } 19 | }; 20 | 21 | $(() => { 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/page.less: -------------------------------------------------------------------------------- 1 | html, body { 2 | overflow-y: auto; 3 | } 4 | @media(min-width: 768px) { 5 | min-height: 945px; 6 | } 7 | 8 | @media(min-width: 768px) { 9 | #bg { 10 | width: 100%; 11 | height: 945px; 12 | position: absolute; 13 | z-index: 0; 14 | } 15 | } 16 | 17 | .register-panel, .login-panel, .forget-password-panel { 18 | margin-top: 100px; 19 | } 20 | 21 | .panel-heading { 22 | padding: 31px 15px; 23 | 24 | .panel-title { 25 | font-size: 24px; 26 | color: #333; 27 | } 28 | } 29 | 30 | .panel-body { 31 | /* max-height: 450px; 32 | overflow-y: auto; */ 33 | padding: 35px 43px; 34 | } 35 | 36 | .tab-content { 37 | margin-top: 15px; 38 | } 39 | 40 | .link-container { 41 | margin-top: 15px; 42 | } 43 | 44 | .form-inline .form-group { 45 | margin-right: 10px; 46 | } -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/templates/forget-password-box.html: -------------------------------------------------------------------------------- 1 | <div id="user-edit-password" style="display: none;"> 2 | <div class="container forget-password-panel"> 3 | <div class="row"> 4 | <div class="col-md-5 col-md-offset-7"> 5 | <div class="panel panel-default"> 6 | <div class="panel-heading"> 7 | <h2 class="panel-title text-center"> 8 | 重置密码 9 | </h2> 10 | </div> 11 | <div class="panel-body"> 12 | <form class="form-horizontal"> 13 | <div class="form-group form-required"> 14 | <label for="username-input" class="col-sm-3 control-label">账号</label> 15 | <div class="col-sm-9"> 16 | <input type="number" class="form-control" id="username-input" placeholder="请填写您的账号(手机号)"> 17 | </div> 18 | </div> 19 | <div class="form-group form-required"> 20 | <label for="password-input" class="col-sm-3 control-label">密码</label> 21 | <div class="col-sm-9"> 22 | <input type="password" class="form-control" id="password-input" placeholder="请填写您的新密码"> 23 | </div> 24 | </div> 25 | <div class="form-group form-required"> 26 | <label for="password-input" class="col-sm-3 control-label">再次输入</label> 27 | <div class="col-sm-9"> 28 | <input type="password" class="form-control" id="password-input" placeholder="请再次填写您的新密码"> 29 | </div> 30 | </div> 31 | <div class="form-group form-required"> 32 | <label for="picvcode-input" class="col-sm-3 control-label">图片验证码</label> 33 | <div class="col-sm-9"> 34 | <div class="input-group"> 35 | <span class="input-group-addon padding0"> 36 | <img class="picvcode" src="data:image/jpeg;base64,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" /> 37 | </span> 38 | <input type="text" class="form-control" id="smsvcode-input" placeholder="填写左侧图片验证码"> 39 | </div> 40 | </div> 41 | </div> 42 | <div class="form-group form-required"> 43 | <label for="smsvcode-input" class="col-sm-3 control-label">短信验证码</label> 44 | <div class="col-sm-9"> 45 | <div class=" input-group"> 46 | <input type="text" class="form-control" id="smsvcode-input" placeholder="请填写短信验证码"> 47 | <span class="input-group-btn"> 48 | <button class="btn btn-info" type="button",>点击获取</button> 49 | </span> 50 | </div> 51 | </div> 52 | </div> 53 | <button class="btn btn-success btn-block" type="button">确认修改密码</button> 54 | </form> 55 | <div class="link-container"> 56 | <div class="text-right"> 57 | <a href="javascript:;" onclick="switchToPage('login')">返回登录页</a> 58 | </div> 59 | </div> 60 | </div> 61 | </div> 62 | </div> 63 | </div> 64 | </div> 65 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/templates/login-box.ejs: -------------------------------------------------------------------------------- 1 | <!-- 登录 --> 2 | <div id="login-box"> 3 | <div class="container login-panel"> 4 | <div class="row"> 5 | <div class="col-md-5 col-md-offset-7"> 6 | <div class="panel panel-default"> 7 | <div class="panel-heading"> 8 | <h2 class="panel-title text-center"> 9 | 欢迎登录XXX管理后台 10 | </h2> 11 | </div> 12 | <div class="panel-body"> 13 | <form class="form-horizontal"> 14 | <div class="form-group"> 15 | <label for="username-input" class="col-sm-2 control-label">账号</label> 16 | <div class="col-sm-10"> 17 | <input type="number" class="form-control" id="username-input" placeholder="请填写您的账号(手机号)"> 18 | </div> 19 | </div> 20 | <div class="form-group"> 21 | <label for="password-input" class="col-sm-2 control-label">密码</label> 22 | <div class="col-sm-10"> 23 | <input type="password" class="form-control" id="password-input" placeholder="请填写您的密码"> 24 | </div> 25 | </div> 26 | <a id="btn-login" class="btn btn-success btn-block" href="<%= constructInsideUrl('index/index') %>">马上登录</a> 27 | </form> 28 | <div class="link-container"> 29 | <div class="text-right"> 30 | <a href="javascript:;" onclick="window.switchToPage('forget-password')">我忘记密码了</a> 31 | </div> 32 | </div> 33 | </div> 34 | </div> 35 | </div> 36 | </div> 37 | </div> 38 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/index/login/templates/main.ejs: -------------------------------------------------------------------------------- 1 | <style type="text/css"> 2 | @media(min-width: 768px) { 3 | #bg { 4 | background: url(<%= BUILD_FILE.images['login-bg'] %>) top center no-repeat; 5 | } 6 | } 7 | </style> 8 | <!--[if lt IE 9]> 9 | <style type="text/css"> 10 | #bg { 11 | background: url(<%= BUILD_FILE.images['login-bg'] %>) top center no-repeat; 12 | } 13 | </style> 14 | <![endif]--> 15 | <div id="bg"> 16 | <%= loginBoxHtml %> 17 | <%= forgetPasswordHtml %> 18 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/user/edit-password/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div ms-controller="user-edit-password"> 3 | <div class="container-fluid"> 4 | <h2 class="page-header"><%= pageTitle %></h2> 5 | <div class="row"> 6 | <div class="col-md-8 col-lg-6"> 7 | <form> 8 | <div class="form-group"> 9 | <label>手机号码(登录时使用的账号)</label> 10 | <input type="number" class="form-control" /> 11 | </div> 12 | <div class="form-group form-required"> 13 | <label>新密码</label> 14 | <input type="password" class="form-control" /> 15 | </div> 16 | <div class="form-group form-required"> 17 | <label>再次输入新密码</label> 18 | <input type="password" class="form-control" /> 19 | </div> 20 | <div class="form-group form-required"> 21 | <label>图片验证码</label> 22 | <div class="input-group"> 23 | <span class="input-group-addon padding0"> 24 | <img class="picvcode" src="data:image/jpeg;base64,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" /> 25 | </span> 26 | <input type="text" class="form-control" placeholder="填写左侧图片验证码"> 27 | </div> 28 | </div> 29 | <div class="form-group form-required"> 30 | <label>短信验证码</label> 31 | <div class="input-group"> 32 | <input type="text" class="form-control" /> 33 | <span class="input-group-btn"> 34 | <button class="btn btn-info" type="button">获取短信验证码</button> 35 | </span> 36 | </div> 37 | </div> 38 | </form> 39 | </div> 40 | </div> 41 | <div class="row"> 42 | <div class="col-md-6 text-center"> 43 | <button id="btn-submit" type="button" class="btn btn-success btn-lg">确认修改密码</button> 44 | </div> 45 | </div> 46 | </div> 47 | </div> 48 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/user/edit-password/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | const pageTitle = '修改密码'; 4 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 5 | -------------------------------------------------------------------------------- /example-admin-1/pages/user/edit-password/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | $(() => { 3 | }); 4 | -------------------------------------------------------------------------------- /example-admin-1/pages/user/modify-info/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div ms-controller="user-modify-info"> 3 | <div class="container-fluid"> 4 | <h2 class="page-header"><%= pageTitle %></h2> 5 | <div class="row"> 6 | <div class="col-md-6"> 7 | <form> 8 | <div class="form-group form-required"> 9 | <label>管理员姓名</label> 10 | <input type="text" class="form-control"> 11 | </div> 12 | <div class="form-group form-required"> 13 | <label>管理员电话</label> 14 | <input type="text" class="form-control"> 15 | </div> 16 | </form> 17 | </div> 18 | </div> 19 | <div class="row"> 20 | <div class="col-md-6 text-center"> 21 | <button id="btn-submit" type="button" class="btn btn-success btn-lg">确认修改</button> 22 | </div> 23 | </div> 24 | </div> 25 | </div> 26 | </div> -------------------------------------------------------------------------------- /example-admin-1/pages/user/modify-info/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | const pageTitle = '修改个人信息'; 4 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 5 | -------------------------------------------------------------------------------- /example-admin-1/pages/user/modify-info/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | 3 | $(() => { 4 | }); 5 | -------------------------------------------------------------------------------- /example-admin-1/public-resource/config/build-file.config.js: -------------------------------------------------------------------------------- 1 | require('!!file-loader?name=index.html!../../index.html'); 2 | const config = require('coreConfigDir/build-file.config'); 3 | module.exports = Object.assign(config, { 4 | images: { 5 | 'login-bg': require('!!file-loader?name=static/images/[name].[ext]!../imgs/login-bg.jpg'), 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /example-admin-1/public-resource/config/common.config.js: -------------------------------------------------------------------------------- 1 | const coreConfig = require('coreConfigDir/common.config'); 2 | const moduleExports = Object.assign(coreConfig, { 3 | DIRS: { 4 | BUILD_FILE: require('configDir/build-file.config'), 5 | }, 6 | 7 | PAGE_ROOT_PATH: '../../', 8 | }); 9 | 10 | /* 帮助确定ie下CORS的代理文件 */ 11 | moduleExports.DIRS.SERVER_API_URL = moduleExports.SERVER_API_URL; 12 | 13 | /* global IS_PRODUCTION:true */ // 由于ESLint会检测没有定义的变量,因此需要这一个`global`注释声明IS_PRODUCTION是一个全局变量(当然在本例中并不是)来规避warning 14 | if (IS_PRODUCTION) { // 由于本脚手架并没有牵涉到HTTP请求,因此此处仅作为演示分离开发/生产环境之用。 15 | moduleExports.API_ROOT = 'http://api.xxxx.com/'; 16 | } else { 17 | moduleExports.API_ROOT = 'http://localhost/mock/'; 18 | } 19 | 20 | module.exports = moduleExports; 21 | -------------------------------------------------------------------------------- /example-admin-1/public-resource/imgs/login-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/example-admin-1/public-resource/imgs/login-bg.jpg -------------------------------------------------------------------------------- /example-admin-1/public-resource/logic/common.page.js: -------------------------------------------------------------------------------- 1 | require('!!bootstrap-webpack!bootstrapConfig'); 2 | // require('metisMenu/metisMenu.min.css'); 3 | require('iconfontDir/iconfont.css'); 4 | require('lessDir/base.less'); 5 | require('metisMenu/metisMenu.min'); 6 | 7 | $(() => { 8 | $('#side-menu').metisMenu(); 9 | $('#side-menu').css('visibility', 'visible'); 10 | (() => { 11 | const width = (window.innerWidth > 0) ? window.innerWidth : window.screen.width; 12 | if (width < 768) { 13 | $('div.navbar-collapse').addClass('collapse'); 14 | // topOffset = 100; // 2-row-menu 15 | const topOffset = $('nav.navbar').height() + 1 + 1; 16 | 17 | let height = ((window.innerHeight > 0) ? window.innerHeight : window.screen.height) - 4; 18 | height = height - topOffset; 19 | if (height < 1) height = 1; 20 | if (height > topOffset) { 21 | $('#page-wrapper').css('min-height', (height) + 'px'); 22 | } 23 | } else { 24 | $('div.navbar-collapse').removeClass('collapse'); 25 | } 26 | 27 | const url = window.location.href; 28 | let element = $('ul.nav a').filter(function filterCb() { 29 | return this.href === url; 30 | }).addClass('active') 31 | .parent('li'); 32 | let ifContinue = true; 33 | while (ifContinue) { 34 | if (element.is('li')) { 35 | element = element.parent('ul').addClass('in') 36 | .parent('li') 37 | .addClass('active'); 38 | } else { 39 | ifContinue = false; 40 | } 41 | } 42 | })(); 43 | 44 | /* 事件绑定 开始 */ 45 | 46 | /* 事件绑定 结束 */ 47 | 48 | /* 各种定时器 开始 */ 49 | /* 各种定时器 结束 */ 50 | }); 51 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/alias.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('./base/dir-vars.config.js'); 3 | module.exports = { 4 | // 模块别名的配置,为了使用方便,一般来说所有模块都是要配置一下别名的 5 | /* 各种目录 */ 6 | configDir: dirVars.configDir, 7 | 8 | /* components */ 9 | 10 | /* logic */ 11 | cm: path.resolve(dirVars.logicDir, 'common.module'), 12 | cp: path.resolve(dirVars.logicDir, 'common.page'), 13 | 14 | /* config */ 15 | configModule: path.resolve(dirVars.configDir, 'common.config'), 16 | }; 17 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/base/dir-vars.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var mE = require('../../../core/webpack-config/base/dir-vars.config.js'); 3 | 4 | /**/mE.srcRootDir = path.resolve(mE.staticRootDir, './example-admin-1'); // 项目业务代码根目录 5 | /**//**/mE.buildDir = path.resolve(mE.srcRootDir, './build'); // 存放编译后生成的所有代码、资源(图片、字体等,虽然只是简单的从源目录迁移过来) 6 | /**//**/mE.pagesDir = path.resolve(mE.srcRootDir, './pages'); // 存放各个页面独有的部分,如入口文件、只有该页面使用到的css、模板文件等 7 | /**//**/mE.publicDir = path.resolve(mE.srcRootDir, './public-resource'); // 存放各个页面使用到的公共资源 8 | /**//**//**/mE.logicDir = path.resolve(mE.publicDir, './logic'); // 存放公用的业务逻辑 9 | /**//**//**/mE.configDir = path.resolve(mE.publicDir, './config'); // 存放项目的各种配置文件 10 | 11 | module.exports = mE; 12 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/base/page-entries.config.js: -------------------------------------------------------------------------------- 1 | var glob = require('glob'); 2 | var dirVars = require('./dir-vars.config.js'); 3 | var options = { 4 | cwd: dirVars.pagesDir, // 在pages目录里找 5 | sync: true, // 这里不能异步,只能同步 6 | }; 7 | var globInstance = new glob.Glob('!(_)*/!(_)*', options); // 考虑到多个页面共用HTML等资源的情况,跳过以'_'开头的目录 8 | 9 | module.exports = globInstance.found; // 一个数组,形如['index/index', 'index/login', 'alert/index'] 10 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/entry.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('./base/dir-vars.config.js'); 3 | var pageArr = require('./base/page-entries.config.js'); 4 | var configEntry = {}; 5 | 6 | pageArr.forEach((page) => { 7 | configEntry[page] = path.resolve(dirVars.pagesDir, page + '/page'); 8 | }); 9 | 10 | module.exports = configEntry; 11 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/module/loaders.config.js: -------------------------------------------------------------------------------- 1 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | const dirVars = require('../base/dir-vars.config.js'); 3 | const includeDirs = [dirVars.srcRootDir]; 4 | 5 | module.exports = [ 6 | { 7 | test: require.resolve('jquery'), 8 | loader: 'expose?$!expose?jQuery', 9 | }, 10 | { 11 | test: /\.css$/, 12 | include: includeDirs, 13 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss'), 14 | }, 15 | { 16 | test: /\.less$/, 17 | include: includeDirs, 18 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss!less'), 19 | }, 20 | { 21 | test: /\.js$/, 22 | include: includeDirs, 23 | loader: 'babel-loader', 24 | query: { 25 | presets: ['es2015-loose'], 26 | cacheDirectory: true, 27 | plugins: ['transform-runtime', 'transform-es3-member-expression-literals', 'transform-es3-property-literals'], 28 | }, 29 | }, 30 | { 31 | test: /\.html$/, 32 | include: includeDirs, 33 | loader: 'html', 34 | }, 35 | { 36 | test: /\.ejs$/, 37 | include: includeDirs, 38 | loader: 'ejs', 39 | }, 40 | { 41 | // 图片加载器,雷同file-loader,更适合图片,可以将较小的图片转成base64,减少http请求 42 | // 如下配置,将小于8192byte的图片转成base64码 43 | test: /\.(png|jpg|gif)$/, 44 | include: includeDirs, 45 | loader: 'url?limit=8192&name=./static/img/[hash].[ext]', 46 | }, 47 | ]; 48 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/module/pre-loaders.config.js: -------------------------------------------------------------------------------- 1 | const dirVars = require('../base/dir-vars.config.js'); 2 | const includeDirs = [dirVars.srcRootDir]; 3 | 4 | module.exports = [{ 5 | test: /\.js$/, 6 | loader: 'eslint', 7 | include: includeDirs, 8 | exclude: [/bootstrap/], 9 | }]; 10 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/output.config.js: -------------------------------------------------------------------------------- 1 | var dirVars = require('./base/dir-vars.config.js'); 2 | module.exports = { 3 | path: dirVars.buildDir, 4 | publicPath: '../../', 5 | filename: '[name]/entry.js', // [name]表示entry每一项中的key,用以批量指定生成后文件的名称 6 | chunkFilename: '[id].bundle.js', 7 | }; 8 | -------------------------------------------------------------------------------- /example-admin-1/webpack-config/plugins.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const pageArr = require('./base/page-entries.config.js'); 4 | const dirVars = require('./base/dir-vars.config.js'); 5 | const configPlugins = []; 6 | 7 | pageArr.forEach((page) => { 8 | const htmlPlugin = new HtmlWebpackPlugin({ 9 | filename: `${page}/page.html`, 10 | template: path.resolve(dirVars.pagesDir, `./${page}/html.js`), 11 | chunks: [page, 'commons/commons'], 12 | hash: true, // 为静态资源生成hash值 13 | xhtml: true, 14 | }); 15 | configPlugins.push(htmlPlugin); 16 | }); 17 | 18 | module.exports = configPlugins; 19 | -------------------------------------------------------------------------------- /example-admin-1/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const config = Object.assign(require('../core/_webpack.dev.config.js'), { 2 | entry: require('./webpack-config/entry.config.js'), 3 | output: require('./webpack-config/output.config.js'), 4 | }); 5 | 6 | config.resolve.alias = Object.assign(config.resolve.alias, require('./webpack-config/alias.config.js')); 7 | config.plugins = config.plugins.concat(require('./webpack-config/plugins.config.js')); 8 | config.module.preLoaders = config.module.preLoaders.concat(require('./webpack-config/module/pre-loaders.config.js')); 9 | config.module.loaders = config.module.loaders.concat(require('./webpack-config/module/loaders.config.js')); 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /example-admin-1/webpack.product.config.js: -------------------------------------------------------------------------------- 1 | const config = Object.assign(require('../core/_webpack.product.config.js'), { 2 | entry: require('./webpack-config/entry.config.js'), 3 | output: require('./webpack-config/output.config.js'), 4 | }); 5 | 6 | config.resolve.alias = Object.assign(config.resolve.alias, require('./webpack-config/alias.config.js')); 7 | config.plugins = config.plugins.concat(require('./webpack-config/plugins.config.js')); 8 | config.module.preLoaders = config.module.preLoaders.concat(require('./webpack-config/module/pre-loaders.config.js')); 9 | config.module.loaders = config.module.loaders.concat(require('./webpack-config/module/loaders.config.js')); 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /example-admin-2/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <script type="text/javascript"> 5 | window.location.href = 'index/index/page.html'; 6 | </script> 7 | </head> 8 | <body> 9 | 10 | </body> 11 | </html> -------------------------------------------------------------------------------- /example-admin-2/npm-scripts/build.script.js: -------------------------------------------------------------------------------- 1 | const clean = require('../../npm-scripts/clean-dirs.script.js'); 2 | const dirVars = require('../webpack-config/base/dir-vars.config.js'); 3 | clean([dirVars.buildDir]); 4 | -------------------------------------------------------------------------------- /example-admin-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-admin-1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node npm-scripts/build.script.js && webpack --progress --colors --bail --config ./webpack.product.config.js", 8 | "dev": "node npm-scripts/build.script.js && webpack --progress --colors --config ./webpack.dev.config.js", 9 | "watch": "node npm-scripts/build.script.js && webpack --progress --colors --watch --config ./webpack.dev.config.js", 10 | "start": "webpack-dev-server --inline --progress --compress --devtool eval --content-base build/ --config ./webpack.dev.config.js", 11 | "profile": "node npm-scripts/build.script.js && webpack --colors --profile --display-modules" 12 | }, 13 | "author": "", 14 | "license": "ISC" 15 | } 16 | -------------------------------------------------------------------------------- /example-admin-2/pages/alert/index/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div class="container-fluid" > 3 | <h2 class="page-header"><%= pageTitle %></h2> 4 | 5 | <div class="row"> 6 | <div class="col-md-12"> 7 | <div class="panel panel-default" id="filter-panel"> 8 | <div class="panel-heading"> 9 | 消息通知筛选条件 10 | </div> 11 | <!-- /.panel-heading --> 12 | <div class="panel-body"> 13 | <form class="form-inline"> 14 | <div class="form-group"> 15 | <label for="status-select">阅读状态:</label> 16 | <select class="form-control" id="status-select"> 17 | <option value="0">全部</option> 18 | <option value="1">已读</option> 19 | <option value="2">未读</option> 20 | </select> 21 | </div> 22 | <button type="button" class="btn btn-primary">确认筛选</button> 23 | </form> 24 | </div> 25 | <!-- /.panel-body --> 26 | </div> 27 | </div> 28 | </div> 29 | 30 | <!-- /.row --> 31 | <div class="row"> 32 | <div class="col-md-12"> 33 | <div class="panel panel-default" id="main-panel"> 34 | <div class="panel-heading"> 35 | 消息通知列表 36 | </div> 37 | <div class="panel-body"> 38 | <div class="table-container table-responsive"> 39 | <table class="table table-hover "> 40 | <thead> 41 | <tr> 42 | <th> 43 | <input type="checkbox"> 44 | </th> 45 | <th>通知时间</th> 46 | <th>内容</th> 47 | </tr> 48 | </thead> 49 | <tbody> 50 | <tr> 51 | <td> 52 | <input type="checkbox" value="543"> 53 | </td> 54 | <td>2016-08-01 08:53</td> 55 | <td>抱歉,您未通过平台审核!原因:2333</td> 56 | </tr> 57 | <tr> 58 | <td> 59 | <input type="checkbox" value="542"> 60 | </td> 61 | <td>2016-08-01 08:52</td> 62 | <td>系统已赠送了抵用券到您的账户,请在指定规则条件内使用。</td> 63 | </tr> 64 | </tbody> 65 | </table> 66 | </div> 67 | <div class="text-center"> 68 | <ul class="paginator-container"></ul> 69 | </div> 70 | </div> 71 | <div class="panel-footer text-center"> 72 | <button type="button" class="btn btn-success">设置为已读</button> 73 | </div> 74 | </div> 75 | </div> 76 | </div> 77 | </div> 78 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/alert/index/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); // 调取存放本页面实际内容的模板文件 2 | const layout = require('layout'); // 调用管理后台内部所使用的布局方案,我在webpack配置里定义其别名为'layout' 3 | const pageTitle = '消息通知'; // 页面名称 4 | 5 | // 给layout传入“页面名称”这一参数(当然有需要的话也可以传入其它参数),同时也传入页面实际内容的HTML字符串。content({ pageTitle })的意思就是把pageTitle作为模板变量传给ejs模板引擎并返回最终生成的HTML字符串。 6 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 7 | -------------------------------------------------------------------------------- /example-admin-2/pages/alert/index/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | $(() => { 3 | 4 | }); 5 | -------------------------------------------------------------------------------- /example-admin-2/pages/index/index/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div class="container-fluid"> 3 | <h2 class="page-header">欢迎进入XXX管理后台!</h2> 4 | </div> 5 | <!-- /.container-fluid --> 6 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/index/index/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | 4 | module.exports = layout.init({ 5 | pageTitle: '', 6 | }).run(content()); 7 | -------------------------------------------------------------------------------- /example-admin-2/pages/index/index/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | const config = require('configModule'); 3 | 4 | $(() => { 5 | /* global IS_PRODUCTION:true */ // 由于ESLint会检测没有定义的变量,因此需要这一个`global`注释声明IS_PRODUCTION是一个全局变量(当然在本例中并不是)来规避warning 6 | if (!IS_PRODUCTION) { 7 | console.log('如果你看到这个Log,那么这个版本实际上是开发用的版本'); 8 | console.log(config.API_ROOT); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/html.js: -------------------------------------------------------------------------------- 1 | const config = require('configModule'); 2 | const noJquery = require('withoutJqueryModule'); 3 | const content = require('./templates/main.ejs'); 4 | const layout = require('layout-without-nav'); 5 | const dirsConfig = config.DIRS; 6 | 7 | const loginBoxHtml = require('./templates/login-box.ejs')({ 8 | constructInsideUrl: noJquery.constructInsideUrl, 9 | }); 10 | const forgetPasswordHtml = require('./templates/forget-password-box.html'); 11 | const renderData = Object.assign(dirsConfig, { loginBoxHtml, forgetPasswordHtml }); 12 | 13 | module.exports = layout.init({ 14 | pageTitle: '', 15 | }).run(content(renderData)); 16 | -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/page.js: -------------------------------------------------------------------------------- 1 | require('!!bootstrap-webpack!bootstrapConfig'); 2 | require('lessDir/base.less'); 3 | require('./page.less'); 4 | 5 | window.switchToPage = (page) => { 6 | switch (page) { 7 | case 'login': 8 | $('#user-edit-password').hide(); 9 | $('#login-box').show(); 10 | break; 11 | 12 | case 'forget-password': 13 | $('#login-box').hide(); 14 | $('#user-edit-password').show(); 15 | break; 16 | 17 | default: 18 | } 19 | }; 20 | 21 | $(() => { 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/page.less: -------------------------------------------------------------------------------- 1 | html, body { 2 | overflow-y: auto; 3 | } 4 | @media(min-width: 768px) { 5 | min-height: 945px; 6 | } 7 | 8 | @media(min-width: 768px) { 9 | #bg { 10 | width: 100%; 11 | height: 945px; 12 | position: absolute; 13 | z-index: 0; 14 | } 15 | } 16 | 17 | .register-panel, .login-panel, .forget-password-panel { 18 | margin-top: 100px; 19 | } 20 | 21 | .panel-heading { 22 | padding: 31px 15px; 23 | 24 | .panel-title { 25 | font-size: 24px; 26 | color: #333; 27 | } 28 | } 29 | 30 | .panel-body { 31 | /* max-height: 450px; 32 | overflow-y: auto; */ 33 | padding: 35px 43px; 34 | } 35 | 36 | .tab-content { 37 | margin-top: 15px; 38 | } 39 | 40 | .link-container { 41 | margin-top: 15px; 42 | } 43 | 44 | .form-inline .form-group { 45 | margin-right: 10px; 46 | } -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/templates/forget-password-box.html: -------------------------------------------------------------------------------- 1 | <div id="user-edit-password" style="display: none;"> 2 | <div class="container forget-password-panel"> 3 | <div class="row"> 4 | <div class="col-md-5 col-md-offset-7"> 5 | <div class="panel panel-default"> 6 | <div class="panel-heading"> 7 | <h2 class="panel-title text-center"> 8 | 重置密码 9 | </h2> 10 | </div> 11 | <div class="panel-body"> 12 | <form class="form-horizontal"> 13 | <div class="form-group form-required"> 14 | <label for="username-input" class="col-sm-3 control-label">账号</label> 15 | <div class="col-sm-9"> 16 | <input type="number" class="form-control" id="username-input" placeholder="请填写您的账号(手机号)"> 17 | </div> 18 | </div> 19 | <div class="form-group form-required"> 20 | <label for="password-input" class="col-sm-3 control-label">密码</label> 21 | <div class="col-sm-9"> 22 | <input type="password" class="form-control" id="password-input" placeholder="请填写您的新密码"> 23 | </div> 24 | </div> 25 | <div class="form-group form-required"> 26 | <label for="password-input" class="col-sm-3 control-label">再次输入</label> 27 | <div class="col-sm-9"> 28 | <input type="password" class="form-control" id="password-input" placeholder="请再次填写您的新密码"> 29 | </div> 30 | </div> 31 | <div class="form-group form-required"> 32 | <label for="picvcode-input" class="col-sm-3 control-label">图片验证码</label> 33 | <div class="col-sm-9"> 34 | <div class="input-group"> 35 | <span class="input-group-addon padding0"> 36 | <img class="picvcode" src="data:image/jpeg;base64,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" /> 37 | </span> 38 | <input type="text" class="form-control" id="smsvcode-input" placeholder="填写左侧图片验证码"> 39 | </div> 40 | </div> 41 | </div> 42 | <div class="form-group form-required"> 43 | <label for="smsvcode-input" class="col-sm-3 control-label">短信验证码</label> 44 | <div class="col-sm-9"> 45 | <div class=" input-group"> 46 | <input type="text" class="form-control" id="smsvcode-input" placeholder="请填写短信验证码"> 47 | <span class="input-group-btn"> 48 | <button class="btn btn-info" type="button",>点击获取</button> 49 | </span> 50 | </div> 51 | </div> 52 | </div> 53 | <button class="btn btn-success btn-block" type="button">确认修改密码</button> 54 | </form> 55 | <div class="link-container"> 56 | <div class="text-right"> 57 | <a href="javascript:;" onclick="switchToPage('login')">返回登录页</a> 58 | </div> 59 | </div> 60 | </div> 61 | </div> 62 | </div> 63 | </div> 64 | </div> 65 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/templates/login-box.ejs: -------------------------------------------------------------------------------- 1 | <!-- 登录 --> 2 | <div id="login-box"> 3 | <div class="container login-panel"> 4 | <div class="row"> 5 | <div class="col-md-5 col-md-offset-7"> 6 | <div class="panel panel-default"> 7 | <div class="panel-heading"> 8 | <h2 class="panel-title text-center"> 9 | 欢迎登录XXX管理后台 10 | </h2> 11 | </div> 12 | <div class="panel-body"> 13 | <form class="form-horizontal"> 14 | <div class="form-group"> 15 | <label for="username-input" class="col-sm-2 control-label">账号</label> 16 | <div class="col-sm-10"> 17 | <input type="number" class="form-control" id="username-input" placeholder="请填写您的账号(手机号)"> 18 | </div> 19 | </div> 20 | <div class="form-group"> 21 | <label for="password-input" class="col-sm-2 control-label">密码</label> 22 | <div class="col-sm-10"> 23 | <input type="password" class="form-control" id="password-input" placeholder="请填写您的密码"> 24 | </div> 25 | </div> 26 | <a id="btn-login" class="btn btn-success btn-block" href="<%= constructInsideUrl('index/index') %>">马上登录</a> 27 | </form> 28 | <div class="link-container"> 29 | <div class="text-right"> 30 | <a href="javascript:;" onclick="window.switchToPage('forget-password')">我忘记密码了</a> 31 | </div> 32 | </div> 33 | </div> 34 | </div> 35 | </div> 36 | </div> 37 | </div> 38 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/index/login/templates/main.ejs: -------------------------------------------------------------------------------- 1 | <style type="text/css"> 2 | @media(min-width: 768px) { 3 | #bg { 4 | background: url(<%= BUILD_FILE.images['login-bg'] %>) top center no-repeat; 5 | } 6 | } 7 | </style> 8 | <!--[if lt IE 9]> 9 | <style type="text/css"> 10 | #bg { 11 | background: url(<%= BUILD_FILE.images['login-bg'] %>) top center no-repeat; 12 | } 13 | </style> 14 | <![endif]--> 15 | <div id="bg"> 16 | <%= loginBoxHtml %> 17 | <%= forgetPasswordHtml %> 18 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/user/edit-password/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div ms-controller="user-edit-password"> 3 | <div class="container-fluid"> 4 | <h2 class="page-header"><%= pageTitle %></h2> 5 | <div class="row"> 6 | <div class="col-md-8 col-lg-6"> 7 | <form> 8 | <div class="form-group"> 9 | <label>手机号码(登录时使用的账号)</label> 10 | <input type="number" class="form-control" /> 11 | </div> 12 | <div class="form-group form-required"> 13 | <label>新密码</label> 14 | <input type="password" class="form-control" /> 15 | </div> 16 | <div class="form-group form-required"> 17 | <label>再次输入新密码</label> 18 | <input type="password" class="form-control" /> 19 | </div> 20 | <div class="form-group form-required"> 21 | <label>图片验证码</label> 22 | <div class="input-group"> 23 | <span class="input-group-addon padding0"> 24 | <img class="picvcode" src="data:image/jpeg;base64,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" /> 25 | </span> 26 | <input type="text" class="form-control" placeholder="填写左侧图片验证码"> 27 | </div> 28 | </div> 29 | <div class="form-group form-required"> 30 | <label>短信验证码</label> 31 | <div class="input-group"> 32 | <input type="text" class="form-control" /> 33 | <span class="input-group-btn"> 34 | <button class="btn btn-info" type="button">获取短信验证码</button> 35 | </span> 36 | </div> 37 | </div> 38 | </form> 39 | </div> 40 | </div> 41 | <div class="row"> 42 | <div class="col-md-6 text-center"> 43 | <button id="btn-submit" type="button" class="btn btn-success btn-lg">确认修改密码</button> 44 | </div> 45 | </div> 46 | </div> 47 | </div> 48 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/user/edit-password/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | const pageTitle = '修改密码'; 4 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 5 | -------------------------------------------------------------------------------- /example-admin-2/pages/user/edit-password/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | $(() => { 3 | }); 4 | -------------------------------------------------------------------------------- /example-admin-2/pages/user/modify-info/content.ejs: -------------------------------------------------------------------------------- 1 | <div id="page-wrapper"> 2 | <div ms-controller="user-modify-info"> 3 | <div class="container-fluid"> 4 | <h2 class="page-header"><%= pageTitle %></h2> 5 | <div class="row"> 6 | <div class="col-md-6"> 7 | <form> 8 | <div class="form-group form-required"> 9 | <label>管理员姓名</label> 10 | <input type="text" class="form-control"> 11 | </div> 12 | <div class="form-group form-required"> 13 | <label>管理员电话</label> 14 | <input type="text" class="form-control"> 15 | </div> 16 | </form> 17 | </div> 18 | </div> 19 | <div class="row"> 20 | <div class="col-md-6 text-center"> 21 | <button id="btn-submit" type="button" class="btn btn-success btn-lg">确认修改</button> 22 | </div> 23 | </div> 24 | </div> 25 | </div> 26 | </div> -------------------------------------------------------------------------------- /example-admin-2/pages/user/modify-info/html.js: -------------------------------------------------------------------------------- 1 | const content = require('./content.ejs'); 2 | const layout = require('layout'); 3 | const pageTitle = '修改个人信息'; 4 | module.exports = layout.init({ pageTitle }).run(content({ pageTitle })); 5 | -------------------------------------------------------------------------------- /example-admin-2/pages/user/modify-info/page.js: -------------------------------------------------------------------------------- 1 | require('cp'); 2 | 3 | $(() => { 4 | }); 5 | -------------------------------------------------------------------------------- /example-admin-2/public-resource/config/build-file.config.js: -------------------------------------------------------------------------------- 1 | require('!!file-loader?name=index.html!../../index.html'); 2 | const config = require('coreConfigDir/build-file.config'); 3 | module.exports = Object.assign(config, { 4 | images: { 5 | 'login-bg': require('!!file-loader?name=static/images/[name].[ext]!../imgs/login-bg.jpg'), 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /example-admin-2/public-resource/config/common.config.js: -------------------------------------------------------------------------------- 1 | const coreConfig = require('coreConfigDir/common.config'); 2 | const moduleExports = Object.assign(coreConfig, { 3 | DIRS: { 4 | BUILD_FILE: require('configDir/build-file.config'), 5 | }, 6 | 7 | PAGE_ROOT_PATH: '../../', 8 | }); 9 | 10 | /* 帮助确定ie下CORS的代理文件 */ 11 | moduleExports.DIRS.SERVER_API_URL = moduleExports.SERVER_API_URL; 12 | 13 | /* global IS_PRODUCTION:true */ // 由于ESLint会检测没有定义的变量,因此需要这一个`global`注释声明IS_PRODUCTION是一个全局变量(当然在本例中并不是)来规避warning 14 | if (IS_PRODUCTION) { // 由于本脚手架并没有牵涉到HTTP请求,因此此处仅作为演示分离开发/生产环境之用。 15 | moduleExports.API_ROOT = 'http://api.xxxx.com/'; 16 | } else { 17 | moduleExports.API_ROOT = 'http://localhost/mock/'; 18 | } 19 | 20 | module.exports = moduleExports; 21 | -------------------------------------------------------------------------------- /example-admin-2/public-resource/imgs/login-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/webpack-seed-v2/cd9944b0bcc1463c384fbd8ec71606fc12953ebf/example-admin-2/public-resource/imgs/login-bg.jpg -------------------------------------------------------------------------------- /example-admin-2/public-resource/logic/common.page.js: -------------------------------------------------------------------------------- 1 | require('!!bootstrap-webpack!bootstrapConfig'); 2 | // require('metisMenu/metisMenu.min.css'); 3 | require('iconfontDir/iconfont.css'); 4 | require('lessDir/base.less'); 5 | require('metisMenu/metisMenu.min'); 6 | 7 | $(() => { 8 | $('#side-menu').metisMenu(); 9 | $('#side-menu').css('visibility', 'visible'); 10 | (() => { 11 | const width = (window.innerWidth > 0) ? window.innerWidth : window.screen.width; 12 | if (width < 768) { 13 | $('div.navbar-collapse').addClass('collapse'); 14 | // topOffset = 100; // 2-row-menu 15 | const topOffset = $('nav.navbar').height() + 1 + 1; 16 | 17 | let height = ((window.innerHeight > 0) ? window.innerHeight : window.screen.height) - 4; 18 | height = height - topOffset; 19 | if (height < 1) height = 1; 20 | if (height > topOffset) { 21 | $('#page-wrapper').css('min-height', (height) + 'px'); 22 | } 23 | } else { 24 | $('div.navbar-collapse').removeClass('collapse'); 25 | } 26 | 27 | const url = window.location.href; 28 | let element = $('ul.nav a').filter(function filterCb() { 29 | return this.href === url; 30 | }).addClass('active') 31 | .parent('li'); 32 | let ifContinue = true; 33 | while (ifContinue) { 34 | if (element.is('li')) { 35 | element = element.parent('ul').addClass('in') 36 | .parent('li') 37 | .addClass('active'); 38 | } else { 39 | ifContinue = false; 40 | } 41 | } 42 | })(); 43 | 44 | /* 事件绑定 开始 */ 45 | 46 | /* 事件绑定 结束 */ 47 | 48 | /* 各种定时器 开始 */ 49 | /* 各种定时器 结束 */ 50 | }); 51 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/alias.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('./base/dir-vars.config.js'); 3 | module.exports = { 4 | // 模块别名的配置,为了使用方便,一般来说所有模块都是要配置一下别名的 5 | /* 各种目录 */ 6 | configDir: dirVars.configDir, 7 | 8 | /* components */ 9 | 10 | /* logic */ 11 | cm: path.resolve(dirVars.logicDir, 'common.module'), 12 | cp: path.resolve(dirVars.logicDir, 'common.page'), 13 | 14 | /* config */ 15 | configModule: path.resolve(dirVars.configDir, 'common.config'), 16 | }; 17 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/base/dir-vars.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var mE = require('../../../core/webpack-config/base/dir-vars.config.js'); 3 | 4 | /**/mE.srcRootDir = path.resolve(mE.staticRootDir, './example-admin-2'); // 项目业务代码根目录 5 | /**//**/mE.buildDir = path.resolve(mE.srcRootDir, './build'); // 存放编译后生成的所有代码、资源(图片、字体等,虽然只是简单的从源目录迁移过来) 6 | /**//**/mE.pagesDir = path.resolve(mE.srcRootDir, './pages'); // 存放各个页面独有的部分,如入口文件、只有该页面使用到的css、模板文件等 7 | /**//**/mE.publicDir = path.resolve(mE.srcRootDir, './public-resource'); // 存放各个页面使用到的公共资源 8 | /**//**//**/mE.logicDir = path.resolve(mE.publicDir, './logic'); // 存放公用的业务逻辑 9 | /**//**//**/mE.configDir = path.resolve(mE.publicDir, './config'); // 存放项目的各种配置文件 10 | 11 | module.exports = mE; 12 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/base/page-entries.config.js: -------------------------------------------------------------------------------- 1 | var glob = require('glob'); 2 | var dirVars = require('./dir-vars.config.js'); 3 | var options = { 4 | cwd: dirVars.pagesDir, // 在pages目录里找 5 | sync: true, // 这里不能异步,只能同步 6 | }; 7 | var globInstance = new glob.Glob('!(_)*/!(_)*', options); // 考虑到多个页面共用HTML等资源的情况,跳过以'_'开头的目录 8 | 9 | module.exports = globInstance.found; // 一个数组,形如['index/index', 'index/login', 'alert/index'] 10 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/entry.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var dirVars = require('./base/dir-vars.config.js'); 3 | var pageArr = require('./base/page-entries.config.js'); 4 | var configEntry = {}; 5 | 6 | pageArr.forEach((page) => { 7 | configEntry[page] = path.resolve(dirVars.pagesDir, page + '/page'); 8 | }); 9 | 10 | module.exports = configEntry; 11 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/module/loaders.config.js: -------------------------------------------------------------------------------- 1 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | const dirVars = require('../base/dir-vars.config.js'); 3 | const includeDirs = [dirVars.srcRootDir]; 4 | 5 | module.exports = [ 6 | { 7 | test: require.resolve('jquery'), 8 | loader: 'expose?$!expose?jQuery', 9 | }, 10 | { 11 | test: /\.css$/, 12 | include: includeDirs, 13 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss'), 14 | }, 15 | { 16 | test: /\.less$/, 17 | include: includeDirs, 18 | loader: ExtractTextPlugin.extract('css?minimize&-autoprefixer!postcss!less'), 19 | }, 20 | { 21 | test: /\.js$/, 22 | include: includeDirs, 23 | loader: 'babel-loader', 24 | query: { 25 | presets: ['es2015-loose'], 26 | cacheDirectory: true, 27 | plugins: ['transform-runtime', 'transform-object-assign'], 28 | }, 29 | }, 30 | { 31 | test: /\.html$/, 32 | include: includeDirs, 33 | loader: 'html', 34 | }, 35 | { 36 | test: /\.ejs$/, 37 | include: includeDirs, 38 | loader: 'ejs', 39 | }, 40 | { 41 | // 图片加载器,雷同file-loader,更适合图片,可以将较小的图片转成base64,减少http请求 42 | // 如下配置,将小于8192byte的图片转成base64码 43 | test: /\.(png|jpg|gif)$/, 44 | include: includeDirs, 45 | loader: 'url?limit=8192&name=./static/img/[hash].[ext]', 46 | }, 47 | ]; 48 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/module/pre-loaders.config.js: -------------------------------------------------------------------------------- 1 | const dirVars = require('../base/dir-vars.config.js'); 2 | const includeDirs = [dirVars.srcRootDir]; 3 | 4 | module.exports = [{ 5 | test: /\.js$/, 6 | loader: 'eslint', 7 | include: includeDirs, 8 | exclude: [/bootstrap/], 9 | }]; 10 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/output.config.js: -------------------------------------------------------------------------------- 1 | var dirVars = require('./base/dir-vars.config.js'); 2 | module.exports = { 3 | path: dirVars.buildDir, 4 | publicPath: '../../', 5 | filename: '[name]/entry.js', // [name]表示entry每一项中的key,用以批量指定生成后文件的名称 6 | chunkFilename: '[id].bundle.js', 7 | }; 8 | -------------------------------------------------------------------------------- /example-admin-2/webpack-config/plugins.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const pageArr = require('./base/page-entries.config.js'); 4 | const dirVars = require('./base/dir-vars.config.js'); 5 | const configPlugins = []; 6 | 7 | pageArr.forEach((page) => { 8 | const htmlPlugin = new HtmlWebpackPlugin({ 9 | filename: `${page}/page.html`, 10 | template: path.resolve(dirVars.pagesDir, `./${page}/html.js`), 11 | chunks: [page, 'commons/commons'], 12 | hash: true, // 为静态资源生成hash值 13 | xhtml: true, 14 | }); 15 | configPlugins.push(htmlPlugin); 16 | }); 17 | 18 | module.exports = configPlugins; 19 | -------------------------------------------------------------------------------- /example-admin-2/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const config = Object.assign(require('../core/_webpack.dev.config.js'), { 2 | entry: require('./webpack-config/entry.config.js'), 3 | output: require('./webpack-config/output.config.js'), 4 | }); 5 | 6 | config.resolve.alias = Object.assign(config.resolve.alias, require('./webpack-config/alias.config.js')); 7 | config.plugins = config.plugins.concat(require('./webpack-config/plugins.config.js')); 8 | config.module.preLoaders = config.module.preLoaders.concat(require('./webpack-config/module/pre-loaders.config.js')); 9 | config.module.loaders = config.module.loaders.concat(require('./webpack-config/module/loaders.config.js')); 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /example-admin-2/webpack.product.config.js: -------------------------------------------------------------------------------- 1 | const config = Object.assign(require('../core/_webpack.product.config.js'), { 2 | entry: require('./webpack-config/entry.config.js'), 3 | output: require('./webpack-config/output.config.js'), 4 | }); 5 | 6 | config.resolve.alias = Object.assign(config.resolve.alias, require('./webpack-config/alias.config.js')); 7 | config.plugins = config.plugins.concat(require('./webpack-config/plugins.config.js')); 8 | config.module.preLoaders = config.module.preLoaders.concat(require('./webpack-config/module/pre-loaders.config.js')); 9 | config.module.loaders = config.module.loaders.concat(require('./webpack-config/module/loaders.config.js')); 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /npm-scripts/clean-dirs.script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const rimraf = require('rimraf'); 5 | const cleanCb = (dir) => () => { 6 | console.log(`目录${dir}已清空`); 7 | }; 8 | 9 | module.exports = (dirs) => { 10 | for (let i = 0; i < dirs.length; i ++) { 11 | rimraf(dirs[i], fs, cleanCb(dirs[i])); 12 | } 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-seed", 3 | "version": "2.0.0", 4 | "description": "This is a seed for web application built by webpack", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.com:Array-Huang/webpack-seed.git" 8 | }, 9 | "author": "Array Huang", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "autoprefixer": "^6.4.0", 13 | "babel-core": "^6.7.7", 14 | "babel-loader": "^6.2.4", 15 | "babel-plugin-transform-es3-member-expression-literals": "^6.8.0", 16 | "babel-plugin-transform-es3-property-literals": "^6.8.0", 17 | "babel-plugin-transform-runtime": "^6.15.0", 18 | "babel-preset-es2015": "^6.9.0", 19 | "babel-preset-es2015-loose": "^7.0.0", 20 | "bootstrap-webpack": "0.0.5", 21 | "css-loader": "^0.23.1", 22 | "ejs-loader": "^0.3.0", 23 | "eslint": "^2.9.0", 24 | "eslint-config-airbnb": "^9.0.0", 25 | "eslint-loader": "^1.3.0", 26 | "eslint-plugin-import": "^1.7.0", 27 | "eslint-plugin-jsx-a11y": "^1.2.0", 28 | "eslint-plugin-react": "^5.0.1", 29 | "exports-loader": "^0.6.3", 30 | "expose-loader": "^0.7.1", 31 | "extract-text-webpack-plugin": "^1.0.1", 32 | "file-loader": "^0.9.0", 33 | "glob": "^7.1.1", 34 | "html-loader": "^0.4.3", 35 | "html-webpack-plugin": "^2.22.0", 36 | "imports-loader": "^0.6.5", 37 | "less": "^2.7.1", 38 | "less-loader": "^2.2.3", 39 | "postcss-loader": "^0.9.1", 40 | "precss": "^1.4.0", 41 | "rimraf": "^2.5.4", 42 | "style-loader": "^0.13.1", 43 | "url-loader": "^0.5.7", 44 | "webpack": "^1.13.2" 45 | }, 46 | "dependencies": { 47 | "babel-runtime": "^6.11.6", 48 | "bootstrap": "^3.3", 49 | "jquery": "^1.12.4" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vendor/ie-fix/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /vendor/ie-fix/jquery.xdomainrequest.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery-ajaxTransport-XDomainRequest - v1.0.4 - 2015-03-05 3 | * https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest 4 | * Copyright (c) 2015 Jason Moon (@JSONMOON) 5 | * Licensed MIT (/blob/master/LICENSE.txt) 6 | */ 7 | (function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else if(typeof exports==='object'){module.exports=a(require('jquery'))}else{a(jQuery)}}(function($){if($.support.cors||!$.ajaxTransport||!window.XDomainRequest){return $}var n=/^(https?:)?\/\//i;var o=/^get|post$/i;var p=new RegExp('^(\/\/|'+location.protocol+')','i');$.ajaxTransport('* text html xml json',function(j,k,l){if(!j.crossDomain||!j.async||!o.test(j.type)||!n.test(j.url)||!p.test(j.url)){return}var m=null;return{send:function(f,g){var h='';var i=(k.dataType||'').toLowerCase();m=new XDomainRequest();if(/^\d+$/.test(k.timeout)){m.timeout=k.timeout}m.ontimeout=function(){g(500,'timeout')};m.onload=function(){var a='Content-Length: '+m.responseText.length+'\r\nContent-Type: '+m.contentType;var b={code:200,message:'success'};var c={text:m.responseText};try{if(i==='html'||/text\/html/i.test(m.contentType)){c.html=m.responseText}else if(i==='json'||(i!=='text'&&/\/json/i.test(m.contentType))){try{c.json=$.parseJSON(m.responseText)}catch(e){b.code=500;b.message='parseerror'}}else if(i==='xml'||(i!=='text'&&/\/xml/i.test(m.contentType))){var d=new ActiveXObject('Microsoft.XMLDOM');d.async=false;try{d.loadXML(m.responseText)}catch(e){d=undefined}if(!d||!d.documentElement||d.getElementsByTagName('parsererror').length){b.code=500;b.message='parseerror';throw'Invalid XML: '+m.responseText;}c.xml=d}}catch(parseMessage){throw parseMessage;}finally{g(b.code,b.message,c,a)}};m.onprogress=function(){};m.onerror=function(){g(500,'error',{text:m.responseText})};if(k.data){h=($.type(k.data)==='string')?k.data:$.param(k.data)}m.open(j.type,j.url);m.send(h)},abort:function(){if(m){m.abort()}}}});return $})); -------------------------------------------------------------------------------- /vendor/ie-fix/respond-proxy.html: -------------------------------------------------------------------------------- 1 | <!-- Respond.js: min/max-width media query polyfill. Remote proxy (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs --> 2 | <!DOCTYPE html> 3 | <html> 4 | <head> 5 | <meta charset="utf-8" /> 6 | <title>Respond JS Proxy 7 | 8 | 9 | 95 | 96 | -------------------------------------------------------------------------------- /vendor/ie-fix/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b' ); 25 | AXO.close(); 26 | iframe = AXO.getElementById( "x" ); 27 | } else { 28 | iframe = doc.createElement( "iframe" ); 29 | iframe.style.cssText = "position:absolute;top:-99em"; 30 | docElem.insertBefore(iframe, docElem.firstElementChild || docElem.firstChild ); 31 | } 32 | 33 | iframe.src = checkBaseURL(proxyURL) + "?url=" + encode(redirectURL) + "&css=" + encode(checkBaseURL(url)); 34 | 35 | function checkFrameName() { 36 | var cssText; 37 | 38 | try { 39 | cssText = iframe.contentWindow.name; 40 | } 41 | catch (e) { } 42 | 43 | if (cssText) { 44 | // We've got what we need. Stop the iframe from loading further content. 45 | iframe.src = "about:blank"; 46 | iframe.parentNode.removeChild(iframe); 47 | iframe = null; 48 | 49 | 50 | // Per http://j.mp/kn9EPh, not taking any chances. Flushing the ActiveXObject 51 | if (AXO) { 52 | AXO = null; 53 | 54 | if (win.CollectGarbage) { 55 | win.CollectGarbage(); 56 | } 57 | } 58 | 59 | callback(cssText); 60 | } 61 | else{ 62 | win.setTimeout(checkFrameName, 100); 63 | } 64 | } 65 | 66 | win.setTimeout(checkFrameName, 500); 67 | } 68 | 69 | // http://stackoverflow.com/a/472729 70 | function checkBaseURL(href) { 71 | var el = document.createElement('div'), 72 | escapedURL = href.split('&').join('&'). 73 | split('<').join('<'). 74 | split('"').join('"'); 75 | 76 | el.innerHTML = 'x'; 77 | return el.firstChild.href; 78 | } 79 | 80 | function checkRedirectURL() { 81 | // IE6 & IE7 don't build out absolute urls in attributes. 82 | // So respond.proxy.gif remains relative instead of http://example.com/respond.proxy.gif. 83 | // This trickery resolves that issue. 84 | if (~ !redirectURL.indexOf(location.host)) { 85 | 86 | var fakeLink = doc.createElement("div"); 87 | 88 | fakeLink.innerHTML = ''; 89 | docElem.insertBefore(fakeLink, docElem.firstElementChild || docElem.firstChild ); 90 | 91 | // Grab the parsed URL from that dummy object 92 | redirectURL = fakeLink.firstChild.href; 93 | 94 | // Clean up 95 | fakeLink.parentNode.removeChild(fakeLink); 96 | fakeLink = null; 97 | } 98 | } 99 | 100 | function buildUrls(){ 101 | var links = doc.getElementsByTagName( "link" ); 102 | 103 | for( var i = 0, linkl = links.length; i < linkl; i++ ){ 104 | 105 | var thislink = links[i], 106 | href = links[i].href, 107 | extreg = (/^([a-zA-Z:]*\/\/(www\.)?)/).test( href ), 108 | ext = (baseElem && !extreg) || extreg; 109 | 110 | //make sure it's an external stylesheet 111 | if( thislink.rel.indexOf( "stylesheet" ) >= 0 && ext ){ 112 | (function( link ){ 113 | fakejax( href, function( css ){ 114 | link.styleSheet.rawCssText = css; 115 | respond.update(); 116 | } ); 117 | })( thislink ); 118 | } 119 | } 120 | 121 | 122 | } 123 | 124 | if( !respond.mediaQueriesSupported ){ 125 | checkRedirectURL(); 126 | buildUrls(); 127 | } 128 | 129 | })( window, document ); 130 | -------------------------------------------------------------------------------- /vendor/ie-fix/xdomain.all.js: -------------------------------------------------------------------------------- 1 | // XHook - v1.3.5 - https://github.com/jpillora/xhook 2 | // Jaime Pillora - MIT Copyright 2016 3 | (function(window,undefined) { 4 | var AFTER, BEFORE, COMMON_EVENTS, EventEmitter, FIRE, FormData, NativeFormData, NativeXMLHttp, OFF, ON, READY_STATE, UPLOAD_EVENTS, XHookFormData, XHookHttpRequest, XMLHTTP, convertHeaders, depricatedProp, document, fakeEvent, mergeObjects, msie, proxyEvents, slice, xhook, _base, 5 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 6 | 7 | document = window.document; 8 | 9 | BEFORE = 'before'; 10 | 11 | AFTER = 'after'; 12 | 13 | READY_STATE = 'readyState'; 14 | 15 | ON = 'addEventListener'; 16 | 17 | OFF = 'removeEventListener'; 18 | 19 | FIRE = 'dispatchEvent'; 20 | 21 | XMLHTTP = 'XMLHttpRequest'; 22 | 23 | FormData = 'FormData'; 24 | 25 | UPLOAD_EVENTS = ['load', 'loadend', 'loadstart']; 26 | 27 | COMMON_EVENTS = ['progress', 'abort', 'error', 'timeout']; 28 | 29 | msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); 30 | 31 | if (isNaN(msie)) { 32 | msie = parseInt((/trident\/.*; rv:(\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); 33 | } 34 | 35 | (_base = Array.prototype).indexOf || (_base.indexOf = function(item) { 36 | var i, x, _i, _len; 37 | for (i = _i = 0, _len = this.length; _i < _len; i = ++_i) { 38 | x = this[i]; 39 | if (x === item) { 40 | return i; 41 | } 42 | } 43 | return -1; 44 | }); 45 | 46 | slice = function(o, n) { 47 | return Array.prototype.slice.call(o, n); 48 | }; 49 | 50 | depricatedProp = function(p) { 51 | return p === "returnValue" || p === "totalSize" || p === "position"; 52 | }; 53 | 54 | mergeObjects = function(src, dst) { 55 | var k, v; 56 | for (k in src) { 57 | v = src[k]; 58 | if (depricatedProp(k)) { 59 | continue; 60 | } 61 | try { 62 | dst[k] = src[k]; 63 | } catch (_error) {} 64 | } 65 | return dst; 66 | }; 67 | 68 | proxyEvents = function(events, src, dst) { 69 | var event, p, _i, _len; 70 | p = function(event) { 71 | return function(e) { 72 | var clone, k, val; 73 | clone = {}; 74 | for (k in e) { 75 | if (depricatedProp(k)) { 76 | continue; 77 | } 78 | val = e[k]; 79 | clone[k] = val === src ? dst : val; 80 | } 81 | return dst[FIRE](event, clone); 82 | }; 83 | }; 84 | for (_i = 0, _len = events.length; _i < _len; _i++) { 85 | event = events[_i]; 86 | if (dst._has(event)) { 87 | src["on" + event] = p(event); 88 | } 89 | } 90 | }; 91 | 92 | fakeEvent = function(type) { 93 | var msieEventObject; 94 | if (document.createEventObject != null) { 95 | msieEventObject = document.createEventObject(); 96 | msieEventObject.type = type; 97 | return msieEventObject; 98 | } else { 99 | try { 100 | return new Event(type); 101 | } catch (_error) { 102 | return { 103 | type: type 104 | }; 105 | } 106 | } 107 | }; 108 | 109 | EventEmitter = function(nodeStyle) { 110 | var emitter, events, listeners; 111 | events = {}; 112 | listeners = function(event) { 113 | return events[event] || []; 114 | }; 115 | emitter = {}; 116 | emitter[ON] = function(event, callback, i) { 117 | events[event] = listeners(event); 118 | if (events[event].indexOf(callback) >= 0) { 119 | return; 120 | } 121 | i = i === undefined ? events[event].length : i; 122 | events[event].splice(i, 0, callback); 123 | }; 124 | emitter[OFF] = function(event, callback) { 125 | var i; 126 | if (event === undefined) { 127 | events = {}; 128 | return; 129 | } 130 | if (callback === undefined) { 131 | events[event] = []; 132 | } 133 | i = listeners(event).indexOf(callback); 134 | if (i === -1) { 135 | return; 136 | } 137 | listeners(event).splice(i, 1); 138 | }; 139 | emitter[FIRE] = function() { 140 | var args, event, i, legacylistener, listener, _i, _len, _ref; 141 | args = slice(arguments); 142 | event = args.shift(); 143 | if (!nodeStyle) { 144 | args[0] = mergeObjects(args[0], fakeEvent(event)); 145 | } 146 | legacylistener = emitter["on" + event]; 147 | if (legacylistener) { 148 | legacylistener.apply(emitter, args); 149 | } 150 | _ref = listeners(event).concat(listeners("*")); 151 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { 152 | listener = _ref[i]; 153 | listener.apply(emitter, args); 154 | } 155 | }; 156 | emitter._has = function(event) { 157 | return !!(events[event] || emitter["on" + event]); 158 | }; 159 | if (nodeStyle) { 160 | emitter.listeners = function(event) { 161 | return slice(listeners(event)); 162 | }; 163 | emitter.on = emitter[ON]; 164 | emitter.off = emitter[OFF]; 165 | emitter.fire = emitter[FIRE]; 166 | emitter.once = function(e, fn) { 167 | var fire; 168 | fire = function() { 169 | emitter.off(e, fire); 170 | return fn.apply(null, arguments); 171 | }; 172 | return emitter.on(e, fire); 173 | }; 174 | emitter.destroy = function() { 175 | return events = {}; 176 | }; 177 | } 178 | return emitter; 179 | }; 180 | 181 | xhook = EventEmitter(true); 182 | 183 | xhook.EventEmitter = EventEmitter; 184 | 185 | xhook[BEFORE] = function(handler, i) { 186 | if (handler.length < 1 || handler.length > 2) { 187 | throw "invalid hook"; 188 | } 189 | return xhook[ON](BEFORE, handler, i); 190 | }; 191 | 192 | xhook[AFTER] = function(handler, i) { 193 | if (handler.length < 2 || handler.length > 3) { 194 | throw "invalid hook"; 195 | } 196 | return xhook[ON](AFTER, handler, i); 197 | }; 198 | 199 | xhook.enable = function() { 200 | window[XMLHTTP] = XHookHttpRequest; 201 | if (NativeFormData) { 202 | window[FormData] = XHookFormData; 203 | } 204 | }; 205 | 206 | xhook.disable = function() { 207 | window[XMLHTTP] = xhook[XMLHTTP]; 208 | if (NativeFormData) { 209 | window[FormData] = NativeFormData; 210 | } 211 | }; 212 | 213 | convertHeaders = xhook.headers = function(h, dest) { 214 | var header, headers, k, name, v, value, _i, _len, _ref; 215 | if (dest == null) { 216 | dest = {}; 217 | } 218 | switch (typeof h) { 219 | case "object": 220 | headers = []; 221 | for (k in h) { 222 | v = h[k]; 223 | name = k.toLowerCase(); 224 | headers.push("" + name + ":\t" + v); 225 | } 226 | return headers.join('\n'); 227 | case "string": 228 | headers = h.split('\n'); 229 | for (_i = 0, _len = headers.length; _i < _len; _i++) { 230 | header = headers[_i]; 231 | if (/([^:]+):\s*(.+)/.test(header)) { 232 | name = (_ref = RegExp.$1) != null ? _ref.toLowerCase() : void 0; 233 | value = RegExp.$2; 234 | if (dest[name] == null) { 235 | dest[name] = value; 236 | } 237 | } 238 | } 239 | return dest; 240 | } 241 | }; 242 | 243 | NativeFormData = window[FormData]; 244 | 245 | XHookFormData = function(form) { 246 | var entries; 247 | this.fd = form ? new NativeFormData(form) : new NativeFormData(); 248 | this.form = form; 249 | entries = []; 250 | Object.defineProperty(this, 'entries', { 251 | get: function() { 252 | var fentries; 253 | fentries = !form ? [] : slice(form.querySelectorAll("input,select")).filter(function(e) { 254 | var _ref; 255 | return ((_ref = e.type) !== 'checkbox' && _ref !== 'radio') || e.checked; 256 | }).map(function(e) { 257 | return [e.name, e.type === "file" ? e.files : e.value]; 258 | }); 259 | return fentries.concat(entries); 260 | } 261 | }); 262 | this.append = (function(_this) { 263 | return function() { 264 | var args; 265 | args = slice(arguments); 266 | entries.push(args); 267 | return _this.fd.append.apply(_this.fd, args); 268 | }; 269 | })(this); 270 | }; 271 | 272 | if (NativeFormData) { 273 | xhook[FormData] = NativeFormData; 274 | window[FormData] = XHookFormData; 275 | } 276 | 277 | NativeXMLHttp = window[XMLHTTP]; 278 | 279 | xhook[XMLHTTP] = NativeXMLHttp; 280 | 281 | XHookHttpRequest = window[XMLHTTP] = function() { 282 | var ABORTED, currentState, emitFinal, emitReadyState, event, facade, hasError, hasErrorHandler, readBody, readHead, request, response, setReadyState, status, transiting, writeBody, writeHead, xhr, _i, _len, _ref; 283 | ABORTED = -1; 284 | xhr = new xhook[XMLHTTP](); 285 | request = {}; 286 | status = null; 287 | hasError = void 0; 288 | transiting = void 0; 289 | response = void 0; 290 | readHead = function() { 291 | var key, name, val, _ref; 292 | response.status = status || xhr.status; 293 | if (!(status === ABORTED && msie < 10)) { 294 | response.statusText = xhr.statusText; 295 | } 296 | if (status !== ABORTED) { 297 | _ref = convertHeaders(xhr.getAllResponseHeaders()); 298 | for (key in _ref) { 299 | val = _ref[key]; 300 | if (!response.headers[key]) { 301 | name = key.toLowerCase(); 302 | response.headers[name] = val; 303 | } 304 | } 305 | } 306 | }; 307 | readBody = function() { 308 | if (!xhr.responseType || xhr.responseType === "text") { 309 | response.text = xhr.responseText; 310 | response.data = xhr.responseText; 311 | } else if (xhr.responseType === "document") { 312 | response.xml = xhr.responseXML; 313 | response.data = xhr.responseXML; 314 | } else { 315 | response.data = xhr.response; 316 | } 317 | if ("responseURL" in xhr) { 318 | response.finalUrl = xhr.responseURL; 319 | } 320 | }; 321 | writeHead = function() { 322 | facade.status = response.status; 323 | facade.statusText = response.statusText; 324 | }; 325 | writeBody = function() { 326 | if ('text' in response) { 327 | facade.responseText = response.text; 328 | } 329 | if ('xml' in response) { 330 | facade.responseXML = response.xml; 331 | } 332 | if ('data' in response) { 333 | facade.response = response.data; 334 | } 335 | if ('finalUrl' in response) { 336 | facade.responseURL = response.finalUrl; 337 | } 338 | }; 339 | emitReadyState = function(n) { 340 | while (n > currentState && currentState < 4) { 341 | facade[READY_STATE] = ++currentState; 342 | if (currentState === 1) { 343 | facade[FIRE]("loadstart", {}); 344 | } 345 | if (currentState === 2) { 346 | writeHead(); 347 | } 348 | if (currentState === 4) { 349 | writeHead(); 350 | writeBody(); 351 | } 352 | facade[FIRE]("readystatechange", {}); 353 | if (currentState === 4) { 354 | setTimeout(emitFinal, 0); 355 | } 356 | } 357 | }; 358 | emitFinal = function() { 359 | if (!hasError) { 360 | facade[FIRE]("load", {}); 361 | } 362 | facade[FIRE]("loadend", {}); 363 | if (hasError) { 364 | facade[READY_STATE] = 0; 365 | } 366 | }; 367 | currentState = 0; 368 | setReadyState = function(n) { 369 | var hooks, process; 370 | if (n !== 4) { 371 | emitReadyState(n); 372 | return; 373 | } 374 | hooks = xhook.listeners(AFTER); 375 | process = function() { 376 | var hook; 377 | if (!hooks.length) { 378 | return emitReadyState(4); 379 | } 380 | hook = hooks.shift(); 381 | if (hook.length === 2) { 382 | hook(request, response); 383 | return process(); 384 | } else if (hook.length === 3 && request.async) { 385 | return hook(request, response, process); 386 | } else { 387 | return process(); 388 | } 389 | }; 390 | process(); 391 | }; 392 | facade = request.xhr = EventEmitter(); 393 | xhr.onreadystatechange = function(event) { 394 | try { 395 | if (xhr[READY_STATE] === 2) { 396 | readHead(); 397 | } 398 | } catch (_error) {} 399 | if (xhr[READY_STATE] === 4) { 400 | transiting = false; 401 | readHead(); 402 | readBody(); 403 | } 404 | setReadyState(xhr[READY_STATE]); 405 | }; 406 | hasErrorHandler = function() { 407 | hasError = true; 408 | }; 409 | facade[ON]('error', hasErrorHandler); 410 | facade[ON]('timeout', hasErrorHandler); 411 | facade[ON]('abort', hasErrorHandler); 412 | facade[ON]('progress', function() { 413 | if (currentState < 3) { 414 | setReadyState(3); 415 | } else { 416 | facade[FIRE]("readystatechange", {}); 417 | } 418 | }); 419 | if ('withCredentials' in xhr || xhook.addWithCredentials) { 420 | facade.withCredentials = false; 421 | } 422 | facade.status = 0; 423 | _ref = COMMON_EVENTS.concat(UPLOAD_EVENTS); 424 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 425 | event = _ref[_i]; 426 | facade["on" + event] = null; 427 | } 428 | facade.open = function(method, url, async, user, pass) { 429 | currentState = 0; 430 | hasError = false; 431 | transiting = false; 432 | request.headers = {}; 433 | request.headerNames = {}; 434 | request.status = 0; 435 | response = {}; 436 | response.headers = {}; 437 | request.method = method; 438 | request.url = url; 439 | request.async = async !== false; 440 | request.user = user; 441 | request.pass = pass; 442 | setReadyState(1); 443 | }; 444 | facade.send = function(body) { 445 | var hooks, k, modk, process, send, _j, _len1, _ref1; 446 | _ref1 = ['type', 'timeout', 'withCredentials']; 447 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 448 | k = _ref1[_j]; 449 | modk = k === "type" ? "responseType" : k; 450 | if (modk in facade) { 451 | request[k] = facade[modk]; 452 | } 453 | } 454 | request.body = body; 455 | send = function() { 456 | var header, value, _k, _len2, _ref2, _ref3; 457 | proxyEvents(COMMON_EVENTS, xhr, facade); 458 | if (facade.upload) { 459 | proxyEvents(COMMON_EVENTS.concat(UPLOAD_EVENTS), xhr.upload, facade.upload); 460 | } 461 | transiting = true; 462 | xhr.open(request.method, request.url, request.async, request.user, request.pass); 463 | _ref2 = ['type', 'timeout', 'withCredentials']; 464 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 465 | k = _ref2[_k]; 466 | modk = k === "type" ? "responseType" : k; 467 | if (k in request) { 468 | xhr[modk] = request[k]; 469 | } 470 | } 471 | _ref3 = request.headers; 472 | for (header in _ref3) { 473 | value = _ref3[header]; 474 | if (header) { 475 | xhr.setRequestHeader(header, value); 476 | } 477 | } 478 | if (request.body instanceof XHookFormData) { 479 | request.body = request.body.fd; 480 | } 481 | xhr.send(request.body); 482 | }; 483 | hooks = xhook.listeners(BEFORE); 484 | process = function() { 485 | var done, hook; 486 | if (!hooks.length) { 487 | return send(); 488 | } 489 | done = function(userResponse) { 490 | if (typeof userResponse === 'object' && (typeof userResponse.status === 'number' || typeof response.status === 'number')) { 491 | mergeObjects(userResponse, response); 492 | if (__indexOf.call(userResponse, 'data') < 0) { 493 | userResponse.data = userResponse.response || userResponse.text; 494 | } 495 | setReadyState(4); 496 | return; 497 | } 498 | process(); 499 | }; 500 | done.head = function(userResponse) { 501 | mergeObjects(userResponse, response); 502 | return setReadyState(2); 503 | }; 504 | done.progress = function(userResponse) { 505 | mergeObjects(userResponse, response); 506 | return setReadyState(3); 507 | }; 508 | hook = hooks.shift(); 509 | if (hook.length === 1) { 510 | return done(hook(request)); 511 | } else if (hook.length === 2 && request.async) { 512 | return hook(request, done); 513 | } else { 514 | return done(); 515 | } 516 | }; 517 | process(); 518 | }; 519 | facade.abort = function() { 520 | status = ABORTED; 521 | if (transiting) { 522 | xhr.abort(); 523 | } else { 524 | facade[FIRE]('abort', {}); 525 | } 526 | }; 527 | facade.setRequestHeader = function(header, value) { 528 | var lName, name; 529 | lName = header != null ? header.toLowerCase() : void 0; 530 | name = request.headerNames[lName] = request.headerNames[lName] || header; 531 | if (request.headers[name]) { 532 | value = request.headers[name] + ', ' + value; 533 | } 534 | request.headers[name] = value; 535 | }; 536 | facade.getResponseHeader = function(header) { 537 | var name; 538 | name = header != null ? header.toLowerCase() : void 0; 539 | return response.headers[name]; 540 | }; 541 | facade.getAllResponseHeaders = function() { 542 | return convertHeaders(response.headers); 543 | }; 544 | if (xhr.overrideMimeType) { 545 | facade.overrideMimeType = function() { 546 | return xhr.overrideMimeType.apply(xhr, arguments); 547 | }; 548 | } 549 | if (xhr.upload) { 550 | facade.upload = request.upload = EventEmitter(); 551 | } 552 | return facade; 553 | }; 554 | 555 | if (typeof define === "function" && define.amd) { 556 | define("xhook", [], function() { 557 | return xhook; 558 | }); 559 | } else { 560 | (this.exports || this).xhook = xhook; 561 | } 562 | 563 | }.call(this,window)); 564 | 565 | // XDomain - v0.7.5 - https://github.com/jpillora/xdomain 566 | // Jaime Pillora - MIT Copyright 2016 567 | (function(window,undefined) { 568 | // XHook - v1.3.5 - https://github.com/jpillora/xhook 569 | // Jaime Pillora - MIT Copyright 2016 570 | (function(window,undefined) { 571 | var AFTER, BEFORE, COMMON_EVENTS, EventEmitter, FIRE, FormData, NativeFormData, NativeXMLHttp, OFF, ON, READY_STATE, UPLOAD_EVENTS, XHookFormData, XHookHttpRequest, XMLHTTP, convertHeaders, depricatedProp, document, fakeEvent, mergeObjects, msie, proxyEvents, slice, xhook, _base, 572 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 573 | 574 | document = window.document; 575 | 576 | BEFORE = 'before'; 577 | 578 | AFTER = 'after'; 579 | 580 | READY_STATE = 'readyState'; 581 | 582 | ON = 'addEventListener'; 583 | 584 | OFF = 'removeEventListener'; 585 | 586 | FIRE = 'dispatchEvent'; 587 | 588 | XMLHTTP = 'XMLHttpRequest'; 589 | 590 | FormData = 'FormData'; 591 | 592 | UPLOAD_EVENTS = ['load', 'loadend', 'loadstart']; 593 | 594 | COMMON_EVENTS = ['progress', 'abort', 'error', 'timeout']; 595 | 596 | msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); 597 | 598 | if (isNaN(msie)) { 599 | msie = parseInt((/trident\/.*; rv:(\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); 600 | } 601 | 602 | (_base = Array.prototype).indexOf || (_base.indexOf = function(item) { 603 | var i, x, _i, _len; 604 | for (i = _i = 0, _len = this.length; _i < _len; i = ++_i) { 605 | x = this[i]; 606 | if (x === item) { 607 | return i; 608 | } 609 | } 610 | return -1; 611 | }); 612 | 613 | slice = function(o, n) { 614 | return Array.prototype.slice.call(o, n); 615 | }; 616 | 617 | depricatedProp = function(p) { 618 | return p === "returnValue" || p === "totalSize" || p === "position"; 619 | }; 620 | 621 | mergeObjects = function(src, dst) { 622 | var k, v; 623 | for (k in src) { 624 | v = src[k]; 625 | if (depricatedProp(k)) { 626 | continue; 627 | } 628 | try { 629 | dst[k] = src[k]; 630 | } catch (_error) {} 631 | } 632 | return dst; 633 | }; 634 | 635 | proxyEvents = function(events, src, dst) { 636 | var event, p, _i, _len; 637 | p = function(event) { 638 | return function(e) { 639 | var clone, k, val; 640 | clone = {}; 641 | for (k in e) { 642 | if (depricatedProp(k)) { 643 | continue; 644 | } 645 | val = e[k]; 646 | clone[k] = val === src ? dst : val; 647 | } 648 | return dst[FIRE](event, clone); 649 | }; 650 | }; 651 | for (_i = 0, _len = events.length; _i < _len; _i++) { 652 | event = events[_i]; 653 | if (dst._has(event)) { 654 | src["on" + event] = p(event); 655 | } 656 | } 657 | }; 658 | 659 | fakeEvent = function(type) { 660 | var msieEventObject; 661 | if (document.createEventObject != null) { 662 | msieEventObject = document.createEventObject(); 663 | msieEventObject.type = type; 664 | return msieEventObject; 665 | } else { 666 | try { 667 | return new Event(type); 668 | } catch (_error) { 669 | return { 670 | type: type 671 | }; 672 | } 673 | } 674 | }; 675 | 676 | EventEmitter = function(nodeStyle) { 677 | var emitter, events, listeners; 678 | events = {}; 679 | listeners = function(event) { 680 | return events[event] || []; 681 | }; 682 | emitter = {}; 683 | emitter[ON] = function(event, callback, i) { 684 | events[event] = listeners(event); 685 | if (events[event].indexOf(callback) >= 0) { 686 | return; 687 | } 688 | i = i === undefined ? events[event].length : i; 689 | events[event].splice(i, 0, callback); 690 | }; 691 | emitter[OFF] = function(event, callback) { 692 | var i; 693 | if (event === undefined) { 694 | events = {}; 695 | return; 696 | } 697 | if (callback === undefined) { 698 | events[event] = []; 699 | } 700 | i = listeners(event).indexOf(callback); 701 | if (i === -1) { 702 | return; 703 | } 704 | listeners(event).splice(i, 1); 705 | }; 706 | emitter[FIRE] = function() { 707 | var args, event, i, legacylistener, listener, _i, _len, _ref; 708 | args = slice(arguments); 709 | event = args.shift(); 710 | if (!nodeStyle) { 711 | args[0] = mergeObjects(args[0], fakeEvent(event)); 712 | } 713 | legacylistener = emitter["on" + event]; 714 | if (legacylistener) { 715 | legacylistener.apply(emitter, args); 716 | } 717 | _ref = listeners(event).concat(listeners("*")); 718 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { 719 | listener = _ref[i]; 720 | listener.apply(emitter, args); 721 | } 722 | }; 723 | emitter._has = function(event) { 724 | return !!(events[event] || emitter["on" + event]); 725 | }; 726 | if (nodeStyle) { 727 | emitter.listeners = function(event) { 728 | return slice(listeners(event)); 729 | }; 730 | emitter.on = emitter[ON]; 731 | emitter.off = emitter[OFF]; 732 | emitter.fire = emitter[FIRE]; 733 | emitter.once = function(e, fn) { 734 | var fire; 735 | fire = function() { 736 | emitter.off(e, fire); 737 | return fn.apply(null, arguments); 738 | }; 739 | return emitter.on(e, fire); 740 | }; 741 | emitter.destroy = function() { 742 | return events = {}; 743 | }; 744 | } 745 | return emitter; 746 | }; 747 | 748 | xhook = EventEmitter(true); 749 | 750 | xhook.EventEmitter = EventEmitter; 751 | 752 | xhook[BEFORE] = function(handler, i) { 753 | if (handler.length < 1 || handler.length > 2) { 754 | throw "invalid hook"; 755 | } 756 | return xhook[ON](BEFORE, handler, i); 757 | }; 758 | 759 | xhook[AFTER] = function(handler, i) { 760 | if (handler.length < 2 || handler.length > 3) { 761 | throw "invalid hook"; 762 | } 763 | return xhook[ON](AFTER, handler, i); 764 | }; 765 | 766 | xhook.enable = function() { 767 | window[XMLHTTP] = XHookHttpRequest; 768 | if (NativeFormData) { 769 | window[FormData] = XHookFormData; 770 | } 771 | }; 772 | 773 | xhook.disable = function() { 774 | window[XMLHTTP] = xhook[XMLHTTP]; 775 | if (NativeFormData) { 776 | window[FormData] = NativeFormData; 777 | } 778 | }; 779 | 780 | convertHeaders = xhook.headers = function(h, dest) { 781 | var header, headers, k, name, v, value, _i, _len, _ref; 782 | if (dest == null) { 783 | dest = {}; 784 | } 785 | switch (typeof h) { 786 | case "object": 787 | headers = []; 788 | for (k in h) { 789 | v = h[k]; 790 | name = k.toLowerCase(); 791 | headers.push("" + name + ":\t" + v); 792 | } 793 | return headers.join('\n'); 794 | case "string": 795 | headers = h.split('\n'); 796 | for (_i = 0, _len = headers.length; _i < _len; _i++) { 797 | header = headers[_i]; 798 | if (/([^:]+):\s*(.+)/.test(header)) { 799 | name = (_ref = RegExp.$1) != null ? _ref.toLowerCase() : void 0; 800 | value = RegExp.$2; 801 | if (dest[name] == null) { 802 | dest[name] = value; 803 | } 804 | } 805 | } 806 | return dest; 807 | } 808 | }; 809 | 810 | NativeFormData = window[FormData]; 811 | 812 | XHookFormData = function(form) { 813 | var entries; 814 | this.fd = form ? new NativeFormData(form) : new NativeFormData(); 815 | this.form = form; 816 | entries = []; 817 | Object.defineProperty(this, 'entries', { 818 | get: function() { 819 | var fentries; 820 | fentries = !form ? [] : slice(form.querySelectorAll("input,select")).filter(function(e) { 821 | var _ref; 822 | return ((_ref = e.type) !== 'checkbox' && _ref !== 'radio') || e.checked; 823 | }).map(function(e) { 824 | return [e.name, e.type === "file" ? e.files : e.value]; 825 | }); 826 | return fentries.concat(entries); 827 | } 828 | }); 829 | this.append = (function(_this) { 830 | return function() { 831 | var args; 832 | args = slice(arguments); 833 | entries.push(args); 834 | return _this.fd.append.apply(_this.fd, args); 835 | }; 836 | })(this); 837 | }; 838 | 839 | if (NativeFormData) { 840 | xhook[FormData] = NativeFormData; 841 | window[FormData] = XHookFormData; 842 | } 843 | 844 | NativeXMLHttp = window[XMLHTTP]; 845 | 846 | xhook[XMLHTTP] = NativeXMLHttp; 847 | 848 | XHookHttpRequest = window[XMLHTTP] = function() { 849 | var ABORTED, currentState, emitFinal, emitReadyState, event, facade, hasError, hasErrorHandler, readBody, readHead, request, response, setReadyState, status, transiting, writeBody, writeHead, xhr, _i, _len, _ref; 850 | ABORTED = -1; 851 | xhr = new xhook[XMLHTTP](); 852 | request = {}; 853 | status = null; 854 | hasError = void 0; 855 | transiting = void 0; 856 | response = void 0; 857 | readHead = function() { 858 | var key, name, val, _ref; 859 | response.status = status || xhr.status; 860 | if (!(status === ABORTED && msie < 10)) { 861 | response.statusText = xhr.statusText; 862 | } 863 | if (status !== ABORTED) { 864 | _ref = convertHeaders(xhr.getAllResponseHeaders()); 865 | for (key in _ref) { 866 | val = _ref[key]; 867 | if (!response.headers[key]) { 868 | name = key.toLowerCase(); 869 | response.headers[name] = val; 870 | } 871 | } 872 | } 873 | }; 874 | readBody = function() { 875 | if (!xhr.responseType || xhr.responseType === "text") { 876 | response.text = xhr.responseText; 877 | response.data = xhr.responseText; 878 | } else if (xhr.responseType === "document") { 879 | response.xml = xhr.responseXML; 880 | response.data = xhr.responseXML; 881 | } else { 882 | response.data = xhr.response; 883 | } 884 | if ("responseURL" in xhr) { 885 | response.finalUrl = xhr.responseURL; 886 | } 887 | }; 888 | writeHead = function() { 889 | facade.status = response.status; 890 | facade.statusText = response.statusText; 891 | }; 892 | writeBody = function() { 893 | if ('text' in response) { 894 | facade.responseText = response.text; 895 | } 896 | if ('xml' in response) { 897 | facade.responseXML = response.xml; 898 | } 899 | if ('data' in response) { 900 | facade.response = response.data; 901 | } 902 | if ('finalUrl' in response) { 903 | facade.responseURL = response.finalUrl; 904 | } 905 | }; 906 | emitReadyState = function(n) { 907 | while (n > currentState && currentState < 4) { 908 | facade[READY_STATE] = ++currentState; 909 | if (currentState === 1) { 910 | facade[FIRE]("loadstart", {}); 911 | } 912 | if (currentState === 2) { 913 | writeHead(); 914 | } 915 | if (currentState === 4) { 916 | writeHead(); 917 | writeBody(); 918 | } 919 | facade[FIRE]("readystatechange", {}); 920 | if (currentState === 4) { 921 | setTimeout(emitFinal, 0); 922 | } 923 | } 924 | }; 925 | emitFinal = function() { 926 | if (!hasError) { 927 | facade[FIRE]("load", {}); 928 | } 929 | facade[FIRE]("loadend", {}); 930 | if (hasError) { 931 | facade[READY_STATE] = 0; 932 | } 933 | }; 934 | currentState = 0; 935 | setReadyState = function(n) { 936 | var hooks, process; 937 | if (n !== 4) { 938 | emitReadyState(n); 939 | return; 940 | } 941 | hooks = xhook.listeners(AFTER); 942 | process = function() { 943 | var hook; 944 | if (!hooks.length) { 945 | return emitReadyState(4); 946 | } 947 | hook = hooks.shift(); 948 | if (hook.length === 2) { 949 | hook(request, response); 950 | return process(); 951 | } else if (hook.length === 3 && request.async) { 952 | return hook(request, response, process); 953 | } else { 954 | return process(); 955 | } 956 | }; 957 | process(); 958 | }; 959 | facade = request.xhr = EventEmitter(); 960 | xhr.onreadystatechange = function(event) { 961 | try { 962 | if (xhr[READY_STATE] === 2) { 963 | readHead(); 964 | } 965 | } catch (_error) {} 966 | if (xhr[READY_STATE] === 4) { 967 | transiting = false; 968 | readHead(); 969 | readBody(); 970 | } 971 | setReadyState(xhr[READY_STATE]); 972 | }; 973 | hasErrorHandler = function() { 974 | hasError = true; 975 | }; 976 | facade[ON]('error', hasErrorHandler); 977 | facade[ON]('timeout', hasErrorHandler); 978 | facade[ON]('abort', hasErrorHandler); 979 | facade[ON]('progress', function() { 980 | if (currentState < 3) { 981 | setReadyState(3); 982 | } else { 983 | facade[FIRE]("readystatechange", {}); 984 | } 985 | }); 986 | if ('withCredentials' in xhr || xhook.addWithCredentials) { 987 | facade.withCredentials = false; 988 | } 989 | facade.status = 0; 990 | _ref = COMMON_EVENTS.concat(UPLOAD_EVENTS); 991 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 992 | event = _ref[_i]; 993 | facade["on" + event] = null; 994 | } 995 | facade.open = function(method, url, async, user, pass) { 996 | currentState = 0; 997 | hasError = false; 998 | transiting = false; 999 | request.headers = {}; 1000 | request.headerNames = {}; 1001 | request.status = 0; 1002 | response = {}; 1003 | response.headers = {}; 1004 | request.method = method; 1005 | request.url = url; 1006 | request.async = async !== false; 1007 | request.user = user; 1008 | request.pass = pass; 1009 | setReadyState(1); 1010 | }; 1011 | facade.send = function(body) { 1012 | var hooks, k, modk, process, send, _j, _len1, _ref1; 1013 | _ref1 = ['type', 'timeout', 'withCredentials']; 1014 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 1015 | k = _ref1[_j]; 1016 | modk = k === "type" ? "responseType" : k; 1017 | if (modk in facade) { 1018 | request[k] = facade[modk]; 1019 | } 1020 | } 1021 | request.body = body; 1022 | send = function() { 1023 | var header, value, _k, _len2, _ref2, _ref3; 1024 | proxyEvents(COMMON_EVENTS, xhr, facade); 1025 | if (facade.upload) { 1026 | proxyEvents(COMMON_EVENTS.concat(UPLOAD_EVENTS), xhr.upload, facade.upload); 1027 | } 1028 | transiting = true; 1029 | xhr.open(request.method, request.url, request.async, request.user, request.pass); 1030 | _ref2 = ['type', 'timeout', 'withCredentials']; 1031 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 1032 | k = _ref2[_k]; 1033 | modk = k === "type" ? "responseType" : k; 1034 | if (k in request) { 1035 | xhr[modk] = request[k]; 1036 | } 1037 | } 1038 | _ref3 = request.headers; 1039 | for (header in _ref3) { 1040 | value = _ref3[header]; 1041 | if (header) { 1042 | xhr.setRequestHeader(header, value); 1043 | } 1044 | } 1045 | if (request.body instanceof XHookFormData) { 1046 | request.body = request.body.fd; 1047 | } 1048 | xhr.send(request.body); 1049 | }; 1050 | hooks = xhook.listeners(BEFORE); 1051 | process = function() { 1052 | var done, hook; 1053 | if (!hooks.length) { 1054 | return send(); 1055 | } 1056 | done = function(userResponse) { 1057 | if (typeof userResponse === 'object' && (typeof userResponse.status === 'number' || typeof response.status === 'number')) { 1058 | mergeObjects(userResponse, response); 1059 | if (__indexOf.call(userResponse, 'data') < 0) { 1060 | userResponse.data = userResponse.response || userResponse.text; 1061 | } 1062 | setReadyState(4); 1063 | return; 1064 | } 1065 | process(); 1066 | }; 1067 | done.head = function(userResponse) { 1068 | mergeObjects(userResponse, response); 1069 | return setReadyState(2); 1070 | }; 1071 | done.progress = function(userResponse) { 1072 | mergeObjects(userResponse, response); 1073 | return setReadyState(3); 1074 | }; 1075 | hook = hooks.shift(); 1076 | if (hook.length === 1) { 1077 | return done(hook(request)); 1078 | } else if (hook.length === 2 && request.async) { 1079 | return hook(request, done); 1080 | } else { 1081 | return done(); 1082 | } 1083 | }; 1084 | process(); 1085 | }; 1086 | facade.abort = function() { 1087 | status = ABORTED; 1088 | if (transiting) { 1089 | xhr.abort(); 1090 | } else { 1091 | facade[FIRE]('abort', {}); 1092 | } 1093 | }; 1094 | facade.setRequestHeader = function(header, value) { 1095 | var lName, name; 1096 | lName = header != null ? header.toLowerCase() : void 0; 1097 | name = request.headerNames[lName] = request.headerNames[lName] || header; 1098 | if (request.headers[name]) { 1099 | value = request.headers[name] + ', ' + value; 1100 | } 1101 | request.headers[name] = value; 1102 | }; 1103 | facade.getResponseHeader = function(header) { 1104 | var name; 1105 | name = header != null ? header.toLowerCase() : void 0; 1106 | return response.headers[name]; 1107 | }; 1108 | facade.getAllResponseHeaders = function() { 1109 | return convertHeaders(response.headers); 1110 | }; 1111 | if (xhr.overrideMimeType) { 1112 | facade.overrideMimeType = function() { 1113 | return xhr.overrideMimeType.apply(xhr, arguments); 1114 | }; 1115 | } 1116 | if (xhr.upload) { 1117 | facade.upload = request.upload = EventEmitter(); 1118 | } 1119 | return facade; 1120 | }; 1121 | 1122 | if (typeof define === "function" && define.amd) { 1123 | define("xhook", [], function() { 1124 | return xhook; 1125 | }); 1126 | } else { 1127 | (this.exports || this).xhook = xhook; 1128 | } 1129 | 1130 | }.call(this,window)); 1131 | var CHECK_INTERVAL, COMPAT_VERSION, XD_CHECK, console, cookies, createSocket, currentOrigin, document, emitter, feature, frames, getFrame, guid, handleSocket, initMaster, initSlave, initdMaster, initdSlave, instOf, jsonEncode, location, log, logger, masters, onMessage, parseUrl, setupEmitter, slaves, slice, sockets, startPostMessage, strip, toRegExp, warn, xdomain, xhook, _i, _len, _ref; 1132 | 1133 | initdMaster = false; 1134 | 1135 | slaves = function(s) { 1136 | var origin, path; 1137 | if (!initdMaster) { 1138 | initMaster(); 1139 | } 1140 | for (origin in s) { 1141 | path = s[origin]; 1142 | log("adding slave: " + origin); 1143 | slaves[origin] = path; 1144 | } 1145 | }; 1146 | 1147 | frames = {}; 1148 | 1149 | getFrame = function(origin, proxyPath) { 1150 | var frame; 1151 | if (frames[origin]) { 1152 | return frames[origin]; 1153 | } 1154 | frame = document.createElement("iframe"); 1155 | frame.id = frame.name = guid(); 1156 | log("creating iframe " + frame.id); 1157 | frame.src = "" + origin + proxyPath; 1158 | frame.setAttribute('style', 'display:none;'); 1159 | document.body.appendChild(frame); 1160 | return frames[origin] = frame.contentWindow; 1161 | }; 1162 | 1163 | initMaster = function() { 1164 | var convertFormData, convertToArrayBuffer, handleRequest; 1165 | initdMaster = true; 1166 | convertToArrayBuffer = function(args, done) { 1167 | var isBlob, isFile, name, obj, reader; 1168 | name = args[0], obj = args[1]; 1169 | isBlob = instOf(obj, 'Blob'); 1170 | isFile = instOf(obj, 'File'); 1171 | if (!(isBlob || isFile)) { 1172 | return 0; 1173 | } 1174 | reader = new FileReader(); 1175 | reader.onload = function() { 1176 | args[1] = null; 1177 | if (isFile) { 1178 | args[2] = obj.name; 1179 | } 1180 | return done(['XD_BLOB', args, this.result, obj.type]); 1181 | }; 1182 | reader.readAsArrayBuffer(obj); 1183 | return 1; 1184 | }; 1185 | convertFormData = function(entries, send) { 1186 | var c; 1187 | entries.forEach(function(args, i) { 1188 | var file, name, value, _i, _len; 1189 | name = args[0], value = args[1]; 1190 | if (instOf(value, 'FileList')) { 1191 | entries.splice(i, 1); 1192 | for (_i = 0, _len = value.length; _i < _len; _i++) { 1193 | file = value[_i]; 1194 | entries.splice(i, 0, [name, file]); 1195 | } 1196 | } 1197 | }); 1198 | c = 0; 1199 | entries.forEach(function(args, i) { 1200 | c += convertToArrayBuffer(args, function(newargs) { 1201 | entries[i] = newargs; 1202 | if (--c === 0) { 1203 | send(); 1204 | } 1205 | }); 1206 | }); 1207 | if (c === 0) { 1208 | send(); 1209 | } 1210 | }; 1211 | handleRequest = function(request, socket) { 1212 | var entries, obj, send; 1213 | socket.on("xhr-event", function() { 1214 | return request.xhr.dispatchEvent.apply(null, arguments); 1215 | }); 1216 | socket.on("xhr-upload-event", function() { 1217 | return request.xhr.upload.dispatchEvent.apply(null, arguments); 1218 | }); 1219 | obj = strip(request); 1220 | obj.headers = request.headers; 1221 | if (request.withCredentials) { 1222 | if (cookies.master) { 1223 | obj.headers[cookies.master] = document.cookie; 1224 | } 1225 | obj.slaveCookie = cookies.slave; 1226 | } 1227 | send = function() { 1228 | return socket.emit("request", obj); 1229 | }; 1230 | if (request.body) { 1231 | obj.body = request.body; 1232 | if (instOf(obj.body, 'FormData')) { 1233 | entries = obj.body.entries; 1234 | obj.body = ["XD_FD", entries]; 1235 | convertFormData(entries, send); 1236 | return; 1237 | } 1238 | } 1239 | send(); 1240 | }; 1241 | if (!('addWithCredentials' in xhook)) { 1242 | xhook.addWithCredentials = true; 1243 | } 1244 | return xhook.before(function(request, callback) { 1245 | var frame, p, socket; 1246 | p = parseUrl(request.url); 1247 | if (!p || p.origin === currentOrigin) { 1248 | return callback(); 1249 | } 1250 | if (!slaves[p.origin]) { 1251 | if (p) { 1252 | log("no slave matching: '" + p.origin + "'"); 1253 | } 1254 | return callback(); 1255 | } 1256 | log("proxying request to slave: '" + p.origin + "'"); 1257 | if (request.async === false) { 1258 | warn("sync not supported"); 1259 | return callback(); 1260 | } 1261 | frame = getFrame(p.origin, slaves[p.origin]); 1262 | socket = createSocket(guid(), frame); 1263 | socket.on("response", function(resp) { 1264 | callback(resp); 1265 | return socket.close(); 1266 | }); 1267 | request.xhr.addEventListener('abort', function() { 1268 | return socket.emit("abort"); 1269 | }); 1270 | if (socket.ready) { 1271 | handleRequest(request, socket); 1272 | } else { 1273 | socket.once('ready', function() { 1274 | return handleRequest(request, socket); 1275 | }); 1276 | } 1277 | }); 1278 | }; 1279 | 1280 | initdSlave = false; 1281 | 1282 | masters = function(m) { 1283 | var origin, path; 1284 | if (!initdSlave) { 1285 | initSlave(); 1286 | } 1287 | for (origin in m) { 1288 | path = m[origin]; 1289 | log("adding master: " + origin); 1290 | masters[origin] = path; 1291 | } 1292 | }; 1293 | 1294 | handleSocket = null; 1295 | 1296 | initSlave = function() { 1297 | initdSlave = true; 1298 | log("handling incoming sockets..."); 1299 | handleSocket = function(origin, socket) { 1300 | var master, masterRegex, pathRegex, regex; 1301 | if (origin === "null") { 1302 | origin = "*"; 1303 | } 1304 | pathRegex = null; 1305 | for (master in masters) { 1306 | regex = masters[master]; 1307 | try { 1308 | masterRegex = toRegExp(master); 1309 | if (masterRegex.test(origin)) { 1310 | pathRegex = toRegExp(regex); 1311 | break; 1312 | } 1313 | } catch (_error) {} 1314 | } 1315 | if (!pathRegex) { 1316 | warn("blocked request from: '" + origin + "'"); 1317 | return; 1318 | } 1319 | socket.once("request", function(req) { 1320 | var args, blob, entries, fd, k, p, v, xhr, _i, _len, _ref; 1321 | log("request: " + req.method + " " + req.url); 1322 | p = parseUrl(req.url); 1323 | if (!(p && pathRegex.test(p.path))) { 1324 | warn("blocked request to path: '" + p.path + "' by regex: " + pathRegex); 1325 | socket.close(); 1326 | return; 1327 | } 1328 | xhr = new XMLHttpRequest(); 1329 | xhr.open(req.method, req.url); 1330 | xhr.addEventListener("*", function(e) { 1331 | return socket.emit('xhr-event', e.type, strip(e)); 1332 | }); 1333 | if (xhr.upload) { 1334 | xhr.upload.addEventListener("*", function(e) { 1335 | return socket.emit('xhr-upload-event', e.type, strip(e)); 1336 | }); 1337 | } 1338 | socket.once("abort", function() { 1339 | return xhr.abort(); 1340 | }); 1341 | xhr.onreadystatechange = function() { 1342 | var resp; 1343 | if (xhr.readyState !== 4) { 1344 | return; 1345 | } 1346 | resp = { 1347 | status: xhr.status, 1348 | statusText: xhr.statusText, 1349 | data: xhr.response, 1350 | headers: xhook.headers(xhr.getAllResponseHeaders()) 1351 | }; 1352 | try { 1353 | resp.text = xhr.responseText; 1354 | } catch (_error) {} 1355 | return socket.emit('response', resp); 1356 | }; 1357 | if (req.withCredentials) { 1358 | xhr.withCredentials = true; 1359 | if (req.slaveCookie) { 1360 | req.headers[req.slaveCookie] = document.cookie; 1361 | } 1362 | } 1363 | if (req.timeout) { 1364 | xhr.timeout = req.timeout; 1365 | } 1366 | if (req.type) { 1367 | xhr.responseType = req.type; 1368 | } 1369 | _ref = req.headers; 1370 | for (k in _ref) { 1371 | v = _ref[k]; 1372 | xhr.setRequestHeader(k, v); 1373 | } 1374 | if (req.body instanceof Array && req.body[0] === "XD_FD") { 1375 | fd = new xhook.FormData(); 1376 | entries = req.body[1]; 1377 | for (_i = 0, _len = entries.length; _i < _len; _i++) { 1378 | args = entries[_i]; 1379 | if (args[0] === "XD_BLOB" && args.length === 4) { 1380 | blob = new Blob([args[2]], { 1381 | type: args[3] 1382 | }); 1383 | args = args[1]; 1384 | args[1] = blob; 1385 | } 1386 | fd.append.apply(fd, args); 1387 | } 1388 | req.body = fd; 1389 | } 1390 | xhr.send(req.body || null); 1391 | }); 1392 | log("slave listening for requests on socket: " + socket.id); 1393 | }; 1394 | if (window === window.parent) { 1395 | return warn("slaves must be in an iframe"); 1396 | } else { 1397 | return window.parent.postMessage("XDPING_" + COMPAT_VERSION, '*'); 1398 | } 1399 | }; 1400 | 1401 | XD_CHECK = "XD_CHECK"; 1402 | 1403 | sockets = {}; 1404 | 1405 | jsonEncode = true; 1406 | 1407 | createSocket = function(id, frame) { 1408 | var check, checks, emit, pendingEmits, ready, sock; 1409 | ready = false; 1410 | sock = sockets[id] = xhook.EventEmitter(true); 1411 | sock.id = id; 1412 | sock.once('close', function() { 1413 | sock.destroy(); 1414 | return sock.close(); 1415 | }); 1416 | pendingEmits = []; 1417 | sock.emit = function() { 1418 | var args, extra; 1419 | args = slice(arguments); 1420 | extra = typeof args[1] === "string" ? " -> " + args[1] : ""; 1421 | log("send socket: " + id + ": " + args[0] + extra); 1422 | args.unshift(id); 1423 | if (ready) { 1424 | emit(args); 1425 | } else { 1426 | pendingEmits.push(args); 1427 | } 1428 | }; 1429 | emit = function(args) { 1430 | if (jsonEncode) { 1431 | args = JSON.stringify(args); 1432 | } 1433 | frame.postMessage(args, "*"); 1434 | }; 1435 | sock.close = function() { 1436 | sock.emit('close'); 1437 | log("close socket: " + id); 1438 | sockets[id] = null; 1439 | }; 1440 | sock.once(XD_CHECK, function(obj) { 1441 | jsonEncode = typeof obj === "string"; 1442 | ready = sock.ready = true; 1443 | sock.emit('ready'); 1444 | log("ready socket: " + id + " (emit #" + pendingEmits.length + " pending)"); 1445 | while (pendingEmits.length) { 1446 | emit(pendingEmits.shift()); 1447 | } 1448 | }); 1449 | checks = 0; 1450 | check = (function(_this) { 1451 | return function() { 1452 | frame.postMessage([id, XD_CHECK, {}], "*"); 1453 | if (ready) { 1454 | return; 1455 | } 1456 | if (checks++ >= xdomain.timeout / CHECK_INTERVAL) { 1457 | warn("Timeout waiting on iframe socket"); 1458 | emitter.fire("timeout"); 1459 | sock.fire("abort"); 1460 | } else { 1461 | setTimeout(check, CHECK_INTERVAL); 1462 | } 1463 | }; 1464 | })(this); 1465 | setTimeout(check); 1466 | log("new socket: " + id); 1467 | return sock; 1468 | }; 1469 | 1470 | onMessage = function(fn) { 1471 | if (document.addEventListener) { 1472 | return window.addEventListener("message", fn); 1473 | } else { 1474 | return window.attachEvent("onmessage", fn); 1475 | } 1476 | }; 1477 | 1478 | startPostMessage = function() { 1479 | return onMessage(function(e) { 1480 | var d, extra, id, sock; 1481 | d = e.data; 1482 | if (typeof d === "string") { 1483 | if (/^XDPING(_(V\d+))?$/.test(d) && RegExp.$2 !== COMPAT_VERSION) { 1484 | return warn("your master is not compatible with your slave, check your xdomain.js version"); 1485 | } else if (/^xdomain-/.test(d)) { 1486 | d = d.split(","); 1487 | } else if (jsonEncode) { 1488 | try { 1489 | d = JSON.parse(d); 1490 | } catch (_error) { 1491 | return; 1492 | } 1493 | } 1494 | } 1495 | if (!(d instanceof Array)) { 1496 | return; 1497 | } 1498 | id = d.shift(); 1499 | if (!/^xdomain-/.test(id)) { 1500 | return; 1501 | } 1502 | sock = sockets[id]; 1503 | if (sock === null) { 1504 | return; 1505 | } 1506 | if (sock === undefined) { 1507 | if (!handleSocket) { 1508 | return; 1509 | } 1510 | sock = createSocket(id, e.source); 1511 | handleSocket(e.origin, sock); 1512 | } 1513 | extra = typeof d[1] === "string" ? " -> " + d[1] : ""; 1514 | log("receive socket: " + id + ": " + d[0] + extra); 1515 | sock.fire.apply(sock, d); 1516 | }); 1517 | }; 1518 | 1519 | 'use strict'; 1520 | 1521 | xhook = (this.exports || this).xhook; 1522 | 1523 | xdomain = function(o) { 1524 | if (!o) { 1525 | return; 1526 | } 1527 | if (o.masters) { 1528 | masters(o.masters); 1529 | } 1530 | if (o.slaves) { 1531 | slaves(o.slaves); 1532 | } 1533 | }; 1534 | 1535 | xdomain.masters = masters; 1536 | 1537 | xdomain.slaves = slaves; 1538 | 1539 | xdomain.debug = false; 1540 | 1541 | xdomain.timeout = 15e3; 1542 | 1543 | CHECK_INTERVAL = 100; 1544 | 1545 | cookies = xdomain.cookies = { 1546 | master: "Master-Cookie", 1547 | slave: "Slave-Cookie" 1548 | }; 1549 | 1550 | document = window.document; 1551 | 1552 | location = window.location; 1553 | 1554 | currentOrigin = xdomain.origin = location.protocol + '//' + location.host; 1555 | 1556 | guid = function() { 1557 | return 'xdomain-' + Math.round(Math.random() * Math.pow(2, 32)).toString(16); 1558 | }; 1559 | 1560 | slice = function(o, n) { 1561 | return Array.prototype.slice.call(o, n); 1562 | }; 1563 | 1564 | console = window.console || {}; 1565 | 1566 | emitter = null; 1567 | 1568 | setupEmitter = function() { 1569 | emitter = xhook.EventEmitter(true); 1570 | xdomain.on = emitter.on; 1571 | xdomain.off = emitter.off; 1572 | }; 1573 | 1574 | if (xhook) { 1575 | setupEmitter(); 1576 | } 1577 | 1578 | logger = function(type) { 1579 | return function(str) { 1580 | str = "xdomain (" + currentOrigin + "): " + str; 1581 | emitter.fire(type, str); 1582 | if (type === 'log' && !xdomain.debug) { 1583 | return; 1584 | } 1585 | if (type in xdomain) { 1586 | xdomain[type](str); 1587 | } else if (type in console) { 1588 | console[type](str); 1589 | } else if (type === 'warn') { 1590 | alert(str); 1591 | } 1592 | }; 1593 | }; 1594 | 1595 | log = logger('log'); 1596 | 1597 | warn = logger('warn'); 1598 | 1599 | _ref = ['postMessage', 'JSON']; 1600 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1601 | feature = _ref[_i]; 1602 | if (!window[feature]) { 1603 | warn("requires '" + feature + "' and this browser does not support it"); 1604 | return; 1605 | } 1606 | } 1607 | 1608 | instOf = function(obj, global) { 1609 | if (!(global in window)) { 1610 | return false; 1611 | } 1612 | return obj instanceof window[global]; 1613 | }; 1614 | 1615 | COMPAT_VERSION = "V1"; 1616 | 1617 | parseUrl = xdomain.parseUrl = function(url) { 1618 | if (/^((https?:)?\/\/[^\/\?]+)(\/.*)?/.test(url)) { 1619 | return { 1620 | origin: (RegExp.$2 ? '' : location.protocol) + RegExp.$1, 1621 | path: RegExp.$3 1622 | }; 1623 | } else { 1624 | log("failed to parse absolute url: " + url); 1625 | return null; 1626 | } 1627 | }; 1628 | 1629 | toRegExp = function(obj) { 1630 | var str; 1631 | if (obj instanceof RegExp) { 1632 | return obj; 1633 | } 1634 | str = obj.toString().replace(/\W/g, function(str) { 1635 | return "\\" + str; 1636 | }).replace(/\\\*/g, ".*"); 1637 | return new RegExp("^" + str + "$"); 1638 | }; 1639 | 1640 | strip = function(src) { 1641 | var dst, k, v, _ref1; 1642 | dst = {}; 1643 | for (k in src) { 1644 | if (k === "returnValue") { 1645 | continue; 1646 | } 1647 | v = src[k]; 1648 | if ((_ref1 = typeof v) !== "function" && _ref1 !== "object") { 1649 | dst[k] = v; 1650 | } 1651 | } 1652 | return dst; 1653 | }; 1654 | 1655 | (function() { 1656 | var attrs, fn, k, prefix, script, _j, _k, _len1, _len2, _ref1, _ref2; 1657 | attrs = { 1658 | debug: function(value) { 1659 | if (typeof value !== "string") { 1660 | return; 1661 | } 1662 | return xdomain.debug = value !== "false"; 1663 | }, 1664 | slave: function(value) { 1665 | var p, s; 1666 | if (!value) { 1667 | return; 1668 | } 1669 | p = parseUrl(value); 1670 | if (!p) { 1671 | return; 1672 | } 1673 | s = {}; 1674 | s[p.origin] = p.path; 1675 | return slaves(s); 1676 | }, 1677 | master: function(value) { 1678 | var m, p; 1679 | if (!value) { 1680 | return; 1681 | } 1682 | if (value === "*") { 1683 | p = { 1684 | origin: "*", 1685 | path: "*" 1686 | }; 1687 | } else { 1688 | p = parseUrl(value); 1689 | } 1690 | if (!p) { 1691 | return; 1692 | } 1693 | m = {}; 1694 | m[p.origin] = p.path.replace(/^\//, "") ? p.path : "*"; 1695 | return masters(m); 1696 | } 1697 | }; 1698 | _ref1 = document.getElementsByTagName("script"); 1699 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 1700 | script = _ref1[_j]; 1701 | if (/xdomain/.test(script.src)) { 1702 | _ref2 = ['', 'data-']; 1703 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 1704 | prefix = _ref2[_k]; 1705 | for (k in attrs) { 1706 | fn = attrs[k]; 1707 | fn(script.getAttribute(prefix + k)); 1708 | } 1709 | } 1710 | } 1711 | } 1712 | })(); 1713 | 1714 | startPostMessage(); 1715 | 1716 | if (typeof define === "function" && define.amd) { 1717 | define("xdomain", ["xhook"], function(xh) { 1718 | xhook = xh; 1719 | setupEmitter(); 1720 | return xdomain; 1721 | }); 1722 | } else { 1723 | (this.exports || this).xdomain = xdomain; 1724 | } 1725 | 1726 | }.call(this,window)); -------------------------------------------------------------------------------- /vendor/metisMenu/metisMenu.min.css: -------------------------------------------------------------------------------- 1 | /* 2 | * metismenu - v1.1.3 3 | * Easy menu jQuery plugin for Twitter Bootstrap 3 4 | * https://github.com/onokumus/metisMenu 5 | * 6 | * Made by Osman Nuri Okumus 7 | * Under MIT License 8 | */ 9 | 10 | .arrow{float:right;line-height:1.42857}.glyphicon.arrow:before{content:"\e079"}.active>a>.glyphicon.arrow:before{content:"\e114"}.fa.arrow:before{content:"\f104"}.active>a>.fa.arrow:before{content:"\f107"}.plus-times{float:right}.fa.plus-times:before{content:"\f067"}.active>a>.fa.plus-times{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.plus-minus{float:right}.fa.plus-minus:before{content:"\f067"}.active>a>.fa.plus-minus:before{content:"\f068"} -------------------------------------------------------------------------------- /vendor/metisMenu/metisMenu.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * metismenu - v1.1.3 3 | * Easy menu jQuery plugin for Twitter Bootstrap 3 4 | * https://github.com/onokumus/metisMenu 5 | * 6 | * Made by Osman Nuri Okumus 7 | * Under MIT License 8 | */ 9 | !function(a,b,c){function d(b,c){this.element=a(b),this.settings=a.extend({},f,c),this._defaults=f,this._name=e,this.init()}var e="metisMenu",f={toggle:!0,doubleTapToGo:!1};d.prototype={init:function(){var b=this.element,d=this.settings.toggle,f=this;this.isIE()<=9?(b.find("li.active").has("ul").children("ul").collapse("show"),b.find("li").not(".active").has("ul").children("ul").collapse("hide")):(b.find("li.active").has("ul").children("ul").addClass("collapse in"),b.find("li").not(".active").has("ul").children("ul").addClass("collapse")),f.settings.doubleTapToGo&&b.find("li.active").has("ul").children("a").addClass("doubleTapToGo"),b.find("li").has("ul").children("a").on("click."+e,function(b){return b.preventDefault(),f.settings.doubleTapToGo&&f.doubleTapToGo(a(this))&&"#"!==a(this).attr("href")&&""!==a(this).attr("href")?(b.stopPropagation(),void(c.location=a(this).attr("href"))):(a(this).parent("li").toggleClass("active").children("ul").collapse("toggle"),void(d&&a(this).parent("li").siblings().removeClass("active").children("ul.in").collapse("hide")))})},isIE:function(){for(var a,b=3,d=c.createElement("div"),e=d.getElementsByTagName("i");d.innerHTML="",e[0];)return b>4?b:a},doubleTapToGo:function(a){var b=this.element;return a.hasClass("doubleTapToGo")?(a.removeClass("doubleTapToGo"),!0):a.parent().children("ul").length?(b.find(".doubleTapToGo").removeClass("doubleTapToGo"),a.addClass("doubleTapToGo"),!1):void 0},remove:function(){this.element.off("."+e),this.element.removeData(e)}},a.fn[e]=function(b){return this.each(function(){var c=a(this);c.data(e)&&c.data(e).remove(),c.data(e,new d(this,b))}),this}}(jQuery,window,document); --------------------------------------------------------------------------------