├── .nvmrc
├── src
├── fonts
│ ├── .gitkeep
│ └── source-sans
│ │ ├── sourcesanspro-black-webfont.eot
│ │ ├── sourcesanspro-black-webfont.ttf
│ │ ├── sourcesanspro-bold-webfont.eot
│ │ ├── sourcesanspro-bold-webfont.ttf
│ │ ├── sourcesanspro-bold-webfont.woff
│ │ ├── sourcesanspro-light-webfont.eot
│ │ ├── sourcesanspro-light-webfont.ttf
│ │ ├── sourcesanspro-black-webfont.woff
│ │ ├── sourcesanspro-black-webfont.woff2
│ │ ├── sourcesanspro-bold-webfont.woff2
│ │ ├── sourcesanspro-italic-webfont.eot
│ │ ├── sourcesanspro-italic-webfont.ttf
│ │ ├── sourcesanspro-italic-webfont.woff
│ │ ├── sourcesanspro-italic-webfont.woff2
│ │ ├── sourcesanspro-light-webfont.woff
│ │ ├── sourcesanspro-light-webfont.woff2
│ │ ├── sourcesanspro-regular-webfont.eot
│ │ ├── sourcesanspro-regular-webfont.ttf
│ │ ├── sourcesanspro-regular-webfont.woff
│ │ ├── sourcesanspro-semibold-webfont.eot
│ │ ├── sourcesanspro-semibold-webfont.ttf
│ │ ├── sourcesanspro-bolditalic-webfont.eot
│ │ ├── sourcesanspro-bolditalic-webfont.ttf
│ │ ├── sourcesanspro-extralight-webfont.eot
│ │ ├── sourcesanspro-extralight-webfont.ttf
│ │ ├── sourcesanspro-regular-webfont.woff2
│ │ ├── sourcesanspro-semibold-webfont.woff
│ │ ├── sourcesanspro-semibold-webfont.woff2
│ │ ├── sourcesanspro-blackitalic-webfont.eot
│ │ ├── sourcesanspro-blackitalic-webfont.ttf
│ │ ├── sourcesanspro-blackitalic-webfont.woff
│ │ ├── sourcesanspro-blackitalic-webfont.woff2
│ │ ├── sourcesanspro-bolditalic-webfont.woff
│ │ ├── sourcesanspro-bolditalic-webfont.woff2
│ │ ├── sourcesanspro-extralight-webfont.woff
│ │ ├── sourcesanspro-extralight-webfont.woff2
│ │ ├── sourcesanspro-lightitalic-webfont.eot
│ │ ├── sourcesanspro-lightitalic-webfont.ttf
│ │ ├── sourcesanspro-lightitalic-webfont.woff
│ │ ├── sourcesanspro-lightitalic-webfont.woff2
│ │ ├── sourcesanspro-semibolditalic-webfont.eot
│ │ ├── sourcesanspro-semibolditalic-webfont.ttf
│ │ ├── sourcesanspro-semibolditalic-webfont.woff
│ │ ├── sourcesanspro-extralightitalic-webfont.eot
│ │ ├── sourcesanspro-extralightitalic-webfont.ttf
│ │ ├── sourcesanspro-extralightitalic-webfont.woff
│ │ ├── sourcesanspro-extralightitalic-webfont.woff2
│ │ └── sourcesanspro-semibolditalic-webfont.woff2
├── styles
│ ├── vendor
│ │ └── .gitkeep
│ ├── base
│ │ ├── base-images.scss
│ │ ├── base-links.scss
│ │ ├── base-colors.scss
│ │ ├── base-hr.scss
│ │ ├── base-code.scss
│ │ ├── base-tables.scss
│ │ ├── base-rhythm.scss
│ │ ├── base-lists.scss
│ │ ├── base-inputs.scss
│ │ └── base-type.scss
│ ├── keyframes
│ │ ├── keyframes-spin.scss
│ │ └── keyframes-pulse.scss
│ ├── utilities-base
│ │ ├── utility-rhythm.scss
│ │ ├── utility-cursor.scss
│ │ ├── utility-radius.scss
│ │ ├── utility-display.scss
│ │ ├── utility-position.scss
│ │ ├── utility-container.scss
│ │ ├── utility-background.scss
│ │ ├── utility-limit.scss
│ │ ├── utility-keep.scss
│ │ ├── utility-box-model.scss
│ │ ├── utility-separate.scss
│ │ ├── utility-fill.scss
│ │ ├── utility-type.scss
│ │ ├── utility-hide.scss
│ │ ├── utility-buffer.scss
│ │ ├── utility-pull.scss
│ │ ├── utility-push.scss
│ │ └── utility-pad.scss
│ ├── mixins
│ │ ├── mixin-clear.scss
│ │ ├── mixin-overflow.scss
│ │ ├── mixin-overlay.scss
│ │ ├── mixin-display.scss
│ │ ├── mixin-radius.scss
│ │ ├── mixin-cursor.scss
│ │ ├── mixin-rhythm.scss
│ │ ├── mixin-keep.scss
│ │ ├── mixin-shadow.scss
│ │ ├── mixin-container.scss
│ │ ├── mixin-font.scss
│ │ ├── mixin-background.scss
│ │ ├── mixin-box-model.scss
│ │ ├── mixin-row.scss
│ │ ├── mixin-animation.scss
│ │ ├── mixin-fill.scss
│ │ ├── mixin-limit.scss
│ │ └── mixin-hide.scss
│ ├── utilities-composed
│ │ ├── utility-row.scss
│ │ ├── utility-inverse.scss
│ │ ├── utility-control.scss
│ │ └── utility-bodytext.scss
│ ├── vendor-overrides
│ │ └── iphone-inline-video.scss
│ ├── transitions
│ │ ├── transition-fade-in.scss
│ │ ├── transition-fade.scss
│ │ ├── transition-flip-vertical.scss
│ │ ├── transition-hide.scss
│ │ └── transition-scale-fade.scss
│ ├── shared.scss
│ ├── definitions
│ │ └── functions.scss
│ ├── global.scss
│ └── webfonts
│ │ └── webfont-source-sans.scss
├── app-icon
│ ├── app-icon.png
│ └── favicon.png
├── vue
│ ├── directives
│ │ ├── images-loaded.js
│ │ └── index.js
│ ├── mixins
│ │ ├── index.js
│ │ └── persist.js
│ └── plugins
│ │ ├── vue-meta.js
│ │ ├── vuex.js
│ │ ├── vue-i18n.js
│ │ ├── index.js
│ │ └── vue-router.js
├── assets
│ └── user-avatar-placeholder.png
├── util
│ ├── startsWith.js
│ ├── eventHasMetaKey.js
│ ├── trimWhitespace.js
│ ├── escapeRegExpString.js
│ ├── scrollToElement.js
│ ├── extractClassnames.js
│ ├── clearSelection.js
│ ├── replaceAll.js
│ ├── getDomainName.js
│ ├── linkIsExternal.js
│ ├── processLargeArray.js
│ ├── index.js
│ └── composeClassnames.js
├── locales
│ ├── number-formats.js
│ ├── index.js
│ ├── date-time-formats.js
│ └── en.js
├── components
│ ├── transitions
│ │ ├── Fade.vue
│ │ ├── FadeIn.vue
│ │ ├── ScaleFade.vue
│ │ ├── FlipVertical.vue
│ │ └── CustomTransition.vue
│ ├── containers
│ │ ├── PanelContent.vue
│ │ ├── Card.vue
│ │ ├── Panel.vue
│ │ └── Page.vue
│ ├── pages
│ │ ├── PageNoAccess.vue
│ │ ├── PageOffline.vue
│ │ ├── PageLogin.vue
│ │ ├── PageNotFound.vue
│ │ ├── PageConsole.vue
│ │ └── PageHome.vue
│ ├── snippets
│ │ ├── InlineIcon.vue
│ │ ├── Pic.vue
│ │ ├── InlineSpinner.vue
│ │ ├── Icon.vue
│ │ ├── Ellipsis.vue
│ │ ├── PicSvg.vue
│ │ └── Spinner.vue
│ ├── console
│ │ ├── ConsoleConfiguration.vue
│ │ ├── ConsoleModels.vue
│ │ └── ConsoleVuex.vue
│ ├── popovers
│ │ ├── PopoverCounter.vue
│ │ └── PopoverMainMenu.vue
│ ├── sections
│ │ ├── FooterSummary.vue
│ │ └── BlankState.vue
│ ├── models
│ │ ├── PostListItem.vue
│ │ └── PostList.vue
│ ├── controls
│ │ ├── IconButton.vue
│ │ └── Click.vue
│ ├── panels
│ │ └── PanelReadme.vue
│ ├── layout
│ │ └── TitlebarMenu.vue
│ └── counters
│ │ ├── LocalCounter.vue
│ │ ├── GlobalCounterIterator.vue
│ │ └── Counter.vue
├── services
│ ├── api
│ │ ├── index.js
│ │ ├── apiAuth.js
│ │ ├── apiPosts.js
│ │ ├── apiUsers.js
│ │ └── apiComments.js
│ ├── panels.js
│ ├── index.js
│ ├── notifications.js
│ ├── network.js
│ ├── time.js
│ ├── env.js
│ ├── auth.js
│ └── menu.js
├── models
│ ├── User.js
│ ├── Comment.js
│ ├── index.js
│ ├── Remote.js
│ ├── Account.js
│ └── Post.js
├── config
│ ├── config.styles.js
│ ├── index.js
│ ├── config.dev.base.js
│ ├── config.dev.routes.js
│ ├── config.aliases.js
│ └── config.routes.js
├── svg
│ ├── check.svg
│ ├── chevron-up.svg
│ ├── chevron-down.svg
│ ├── chevron-left.svg
│ ├── chevron-right.svg
│ ├── cross.svg
│ ├── hamburger.svg
│ ├── chevron-left-left.svg
│ ├── chevron-right-right.svg
│ ├── arrow-left.svg
│ ├── arrow-right.svg
│ ├── power.svg
│ └── app-icon.svg
├── .htmllintrc
└── store
│ └── index.js
├── static
└── .gitkeep
├── .eslintignore
├── tooling
├── env
│ ├── prod.env.js
│ ├── test.env.js
│ ├── dev.env.js
│ └── index.js
├── unit
│ ├── .eslintrc
│ ├── index.js
│ └── karma.conf.js
├── dev-client.js
├── vue-loader.conf.js
├── e2e
│ ├── nightwatch.chrome.globals.js
│ ├── nightwatch.globals.js
│ ├── nightwatch.chrome.conf.js
│ ├── custom-assertions
│ │ └── elementCount.js
│ ├── nightwatch.conf.js
│ └── runner.js
├── webpack.test.conf.js
├── build.js
├── check-versions.js
└── custom-config.js
├── .gitignore
├── docco.json
├── .stylelintrc
├── docs
└── .gitignore
├── reports
└── .gitignore
├── Dockerfile
├── README.md
├── jsconfig.json
├── .editorconfig
├── spec
├── .eslintrc.js
├── components
│ └── pages
│ │ └── PageHome.spec.js
├── e2e
│ └── appContentVisibleAtStart.spec.js
├── store
│ └── mutations
│ │ └── incrementCounter.spec.js
├── services
│ └── env.spec.js
├── models
│ └── Post.spec.js
└── util
│ ├── trimWhitespace.spec.js
│ └── getDomainName.spec.js
├── .babelrc
├── postcss.config.js
├── .vscode
├── launch.json
└── tasks.json
├── .eslintrc.js
├── .markdownlint.json
└── LICENSE
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.1.2
2 |
--------------------------------------------------------------------------------
/src/fonts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | tooling/*.js
2 |
--------------------------------------------------------------------------------
/tooling/env/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/base/base-images.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | // img,
5 | // svg {
6 | // }
7 |
--------------------------------------------------------------------------------
/src/styles/base/base-links.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | a {
5 | color: inherit;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app-icon/app-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/app-icon/app-icon.png
--------------------------------------------------------------------------------
/src/app-icon/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/app-icon/favicon.png
--------------------------------------------------------------------------------
/src/vue/directives/images-loaded.js:
--------------------------------------------------------------------------------
1 |
2 | import ImagesLoaded from 'vue-images-loaded';
3 | export default ImagesLoaded;
4 |
--------------------------------------------------------------------------------
/src/assets/user-avatar-placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/assets/user-avatar-placeholder.png
--------------------------------------------------------------------------------
/src/styles/keyframes/keyframes-spin.scss:
--------------------------------------------------------------------------------
1 |
2 | @keyframes spin {
3 |
4 | 100% {
5 | transform: rotate(360deg);
6 | }
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | selenium-debug.log
8 | .idea
9 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-rhythm.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .no-rhythm {
7 | @include no-rhythm;
8 | }
9 |
--------------------------------------------------------------------------------
/src/util/startsWith.js:
--------------------------------------------------------------------------------
1 | export default function (string, substring) {
2 | return string.lastIndexOf(substring, 0) === 0 ? true : false;
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/base/base-colors.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | html {
5 | color: $color-dark;
6 | background-color: $color-verylightgrey;
7 | }
8 |
--------------------------------------------------------------------------------
/tooling/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-black-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-black-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-black-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-black-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bold-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bold-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bold-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bold-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-light-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-light-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-light-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-light-webfont.ttf
--------------------------------------------------------------------------------
/docco.json:
--------------------------------------------------------------------------------
1 | {
2 | ".vue": {
3 | "name": "html",
4 | "symbol": "(//)"
5 | },
6 | ".html": {
7 | "name": "html",
8 | "symbol": "(//)"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-black-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-black-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-black-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-black-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bold-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bold-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-italic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-italic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-italic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-italic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-italic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-italic-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-italic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-italic-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-light-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-light-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-light-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-light-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-regular-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-regular-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-regular-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-regular-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-regular-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibold-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibold-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibold-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralight-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralight-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralight-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralight-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-regular-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-regular-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibold-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibold-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibold-webfont.woff2
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-clear.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin clear-after {
3 | *zoom: 1;
4 | &:after {
5 | content: ' ';
6 | display: table;
7 | clear: both;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-blackitalic-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-bolditalic-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralight-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralight-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralight-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralight-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-lightitalic-webfont.woff2
--------------------------------------------------------------------------------
/tooling/env/test.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var devEnv = require('./dev.env')
3 |
4 | module.exports = merge(devEnv, {
5 | NODE_ENV: '"testing"'
6 | })
7 |
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.woff
--------------------------------------------------------------------------------
/src/styles/base/base-hr.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | hr {
5 | border-width: 1px 0 0 0;
6 | border-style: solid;
7 | border-color: $color-dark-translucent;
8 | }
9 |
--------------------------------------------------------------------------------
/src/vue/directives/index.js:
--------------------------------------------------------------------------------
1 |
2 | import ImagesLoaded from './images-loaded';
3 |
4 | export {
5 | ImagesLoaded
6 | };
7 |
8 | export default {
9 | ImagesLoaded
10 | };
11 |
--------------------------------------------------------------------------------
/tooling/env/dev.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var prodEnv = require('./prod.env')
3 |
4 | module.exports = merge(prodEnv, {
5 | NODE_ENV: '"development"'
6 | })
7 |
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-extralightitalic-webfont.woff2
--------------------------------------------------------------------------------
/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jerryjappinen/bellevue-v1/HEAD/src/fonts/source-sans/sourcesanspro-semibolditalic-webfont.woff2
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "processors": [
3 | "stylelint-processor-html"
4 | ],
5 | "extends": "stylelint-config-standard",
6 | "rules": {
7 | "no-empty-source": null
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/styles/utilities-composed/utility-row.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .row {
7 | @include row;
8 | }
9 |
10 | .row-content {
11 | @include row-content;
12 | }
13 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory, except this file.
2 | *
3 |
4 | # see: http://stackoverflow.com/questions/115983/how-do-i-add-an-empty-directory-to-a-git-repository
5 | !.gitignore
6 |
--------------------------------------------------------------------------------
/src/util/eventHasMetaKey.js:
--------------------------------------------------------------------------------
1 | // Check if keyboard event object includes meta key being pressed
2 | export default function (event) {
3 | return (event.ctrlKey || event.metaKey || event.shiftKey);
4 | }
5 |
--------------------------------------------------------------------------------
/reports/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory, except this file.
2 | *
3 |
4 | # see: http://stackoverflow.com/questions/115983/how-do-i-add-an-empty-directory-to-a-git-repository
5 | !.gitignore
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM kyma/docker-nginx
2 |
3 | # Example if you wanna swap the default server file.
4 | # COPY path/to/your/default /etc/nginx/sites-enabled/default
5 |
6 | COPY dist/ /var/www
7 |
8 | CMD 'nginx'
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bellevue
2 |
3 | Bellevue is a full-featured frontend project template for modern single-page applications built on Vue.js and Webpack.
4 |
5 | **THIS REPO has moved: https://github.com/Eiskis/bellevue**
6 |
--------------------------------------------------------------------------------
/src/util/trimWhitespace.js:
--------------------------------------------------------------------------------
1 | import { trim } from 'lodash';
2 |
3 | // Condence all whitespace in a string to maximum of one space
4 | export default function (string) {
5 | return trim(string).replace(/\s+/g, ' ');
6 | }
7 |
--------------------------------------------------------------------------------
/src/util/escapeRegExpString.js:
--------------------------------------------------------------------------------
1 | // https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
2 | export default function (string) {
3 | return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
4 | }
5 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-overflow.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin overflow-vertical {
3 | overflow-x: hidden;
4 | overflow-y: auto;
5 | }
6 |
7 | @mixin overflow-hidden {
8 | overflow: hidden;
9 | -webkit-overflow-scrolling: auto;
10 | }
11 |
--------------------------------------------------------------------------------
/src/locales/number-formats.js:
--------------------------------------------------------------------------------
1 |
2 | // See details: https://kazupon.github.io/vue-i18n/en/number.html
3 | export default {
4 |
5 | 'en': {
6 | currency: {
7 | style: 'currency',
8 | currency: 'USD'
9 | }
10 | }
11 |
12 | };
13 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "experimentalDecorators": true,
5 | "allowSyntheticDefaultImports": true
6 | },
7 | "include": [
8 | "spec/**/*.js",
9 | "src/**/*.js",
10 | "src/**/*.vue"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/vendor-overrides/iphone-inline-video.scss:
--------------------------------------------------------------------------------
1 |
2 | // @import '~@shared-styles';
3 |
4 | .IIV::-webkit-media-controls-play-button,
5 | .IIV::-webkit-media-controls-start-playback-button {
6 | pointer-events: none;
7 | width: 5px;
8 | opacity: 0;
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = tab
6 | # indent_size = 4
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.{yml,yaml}]
12 | indent_style = space
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/src/util/scrollToElement.js:
--------------------------------------------------------------------------------
1 | import naturalScroll from 'natural-scroll';
2 |
3 | export default function (container, element, duration, offset) {
4 | let position = element.offsetTop - (offset ? offset : 0);
5 | naturalScroll.scrollTop(container, position, duration);
6 | }
7 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-overlay.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin overlay ($direction: 0deg, $opacity: 0.25, $color: $color-black) {
3 | background-image: linear-gradient(
4 | $direction,
5 | rgba($color, 0),
6 | rgba($color, ($opacity / 2)) 60%,
7 | rgba($color, $opacity)
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/vue/mixins/index.js:
--------------------------------------------------------------------------------
1 |
2 | import persist from './persist';
3 |
4 | export const local = {};
5 |
6 | export const global = {
7 | persist
8 | };
9 |
10 | export {
11 | // someMixin
12 | persist
13 | };
14 |
15 | export default {
16 | // someMixin
17 | persist
18 | };
19 |
--------------------------------------------------------------------------------
/src/util/extractClassnames.js:
--------------------------------------------------------------------------------
1 | // Pick the keys of a classname hash whose values are truthy
2 | export default function (stateHash) {
3 | var classes = [];
4 | for (var key in stateHash) {
5 | if (stateHash[key]) {
6 | classes.push(key);
7 | }
8 | }
9 | return classes;
10 | }
11 |
--------------------------------------------------------------------------------
/spec/.eslintrc.js:
--------------------------------------------------------------------------------
1 |
2 | var _ = require('lodash');
3 | var base = require('../src/.eslintrc');
4 |
5 | module.exports = _.merge(
6 | {},
7 | base,
8 | {
9 | 'env': {
10 | 'mocha': true
11 | },
12 | 'globals': {
13 | 'expect': true,
14 | 'sinon': true
15 | }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-2"
5 | ],
6 | "plugins": ["transform-runtime"],
7 | "comments": false,
8 | "env": {
9 | "test": {
10 | "presets": ["env", "stage-2"],
11 | "plugins": [ "istanbul" ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tooling/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('eventsource-polyfill')
3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
4 |
5 | hotClient.subscribe(function (event) {
6 | if (event.action === 'reload') {
7 | window.location.reload()
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/src/util/clearSelection.js:
--------------------------------------------------------------------------------
1 | export default function () {
2 |
3 | if (window.document.selection && window.document.selection.empty) {
4 | window.document.selection.empty();
5 |
6 | } else if (window.getSelection) {
7 | var sel = window.getSelection();
8 | sel.removeAllRanges();
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-cursor.scss:
--------------------------------------------------------------------------------
1 |
2 | .cursor-grab {
3 | cursor: grab;
4 | }
5 |
6 | .cursor-pointer {
7 | cursor: pointer;
8 | }
9 |
10 | .cursor-text {
11 | cursor: text;
12 | }
13 |
14 | .cursor-default {
15 | cursor: default;
16 | }
17 |
18 | .cursor-inherit {
19 | cursor: inherit;
20 | }
21 |
--------------------------------------------------------------------------------
/src/locales/index.js:
--------------------------------------------------------------------------------
1 |
2 | import en from './en';
3 |
4 | import dateTimeFormats from './date-time-formats';
5 | import numberFormats from './number-formats';
6 |
7 | export default {
8 |
9 | dateTimeFormats: dateTimeFormats,
10 | numberFormats: numberFormats,
11 |
12 | messages: {
13 | en
14 | }
15 |
16 | };
17 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-display.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin inline {
3 | display: inline;
4 | }
5 |
6 | @mixin inline-block {
7 | display: inline-block;
8 | }
9 |
10 | @mixin block {
11 | display: block;
12 | }
13 |
14 | @mixin flex {
15 | display: flex;
16 | }
17 |
18 | @mixin inline-flex {
19 | display: inline-flex;
20 | }
21 |
--------------------------------------------------------------------------------
/src/vue/plugins/vue-meta.js:
--------------------------------------------------------------------------------
1 |
2 | // Handling meta tags and
per component
3 | // https://github.com/declandewet/vue-meta
4 | import Vue from 'vue';
5 | import VueMeta from 'vue-meta';
6 |
7 | // Import route components for vue-router
8 | import config from '@config';
9 |
10 | Vue.use(VueMeta, config.metaInfo);
11 |
12 | export default VueMeta;
13 |
--------------------------------------------------------------------------------
/src/components/transitions/Fade.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tooling/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var config = require('./env')
3 | var isProduction = process.env.NODE_ENV === 'production'
4 |
5 | module.exports = {
6 | loaders: utils.cssLoaders({
7 | sourceMap: isProduction
8 | ? config.build.productionSourceMap
9 | : config.dev.cssSourceMap,
10 | extract: isProduction
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/transitions/FadeIn.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-radius.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin radius ($rad: $radius) {
3 | border-radius: $rad;
4 | }
5 |
6 | @mixin radius-tight {
7 | border-radius: $radius-tight;
8 | }
9 |
10 | @mixin radius-loose {
11 | border-radius: $radius-loose;
12 | }
13 |
14 | @mixin radius-round {
15 | border-radius: 1000em;
16 | }
17 |
18 | @mixin no-radius {
19 | border-radius: 0;
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-radius.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .radius {
7 | @include radius;
8 | }
9 |
10 | .radius-tight {
11 | @include radius-tight;
12 | }
13 |
14 | .radius-loose {
15 | @include radius-loose;
16 | }
17 |
18 | .radius-round {
19 | @include radius-round;
20 | }
21 |
22 | .no-radius {
23 | @include radius-round;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/transitions/ScaleFade.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/transitions/FlipVertical.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 | // https://github.com/postcss/postcss-scss/issues/49
3 |
4 | module.exports = {
5 | 'syntax': require('postcss-scss'),
6 | // 'syntax': 'postcss-scss',
7 | 'parser': 'scss',
8 | 'plugins': {
9 | // to edit target browsers: use 'browserlist' field in package.json
10 | 'autoprefixer': {}
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/api/index.js:
--------------------------------------------------------------------------------
1 | import { merge } from 'lodash';
2 | import Vue from 'vue';
3 |
4 | import api from './api';
5 | import apiAuth from './apiAuth';
6 | import apiComments from './apiComments';
7 | import apiPosts from './apiPosts';
8 | import apiUsers from './apiUsers';
9 |
10 | export default new Vue(merge(
11 | api,
12 | apiAuth,
13 | apiComments,
14 | apiPosts,
15 | apiUsers
16 | ));
17 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-cursor.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin cursor-grab {
3 | cursor: grab;
4 | }
5 |
6 | @mixin cursor-grabbing {
7 | cursor: grabbing;
8 | }
9 |
10 | @mixin cursor-pointer {
11 | cursor: pointer;
12 | }
13 |
14 | @mixin cursor-text {
15 | cursor: text;
16 | }
17 |
18 | @mixin cursor-default {
19 | cursor: default;
20 | }
21 |
22 | @mixin cursor-inherit {
23 | cursor: inherit;
24 | }
25 |
--------------------------------------------------------------------------------
/tooling/e2e/nightwatch.chrome.globals.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash')
2 | var chromedriver = require('chromedriver')
3 |
4 | var defaults = require('./nightwatch.globals.js')
5 |
6 | module.exports = _.merge(defaults, {
7 |
8 | before: function (done) {
9 | chromedriver.start()
10 | done()
11 | },
12 |
13 | after: function (done) {
14 | chromedriver.stop()
15 | done()
16 | }
17 |
18 | })
19 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-display.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // NOTE: order is important: blockiest classes last so they override inliny classes
7 |
8 | .inline {
9 | @include inline;
10 | }
11 |
12 | .inline-block {
13 | @include inline-block;
14 | }
15 |
16 | .block {
17 | @include block;
18 | }
19 |
20 | .flex {
21 | @include flex;
22 | }
23 |
24 | .inline-flex {
25 | @include inline-flex;
26 | }
27 |
--------------------------------------------------------------------------------
/src/util/replaceAll.js:
--------------------------------------------------------------------------------
1 | import escapeRegExpString from './escapeRegExpString';
2 |
3 | export default function (string, search, replacement) {
4 |
5 | if (search && (typeof search === 'string' || typeof search === 'number')) {
6 | search = '' + search;
7 |
8 | if (!replacement) {
9 | replacement = '';
10 | }
11 |
12 | return string.replace(new RegExp(escapeRegExpString(search), 'g'), replacement);
13 | }
14 |
15 | return string;
16 | }
17 |
--------------------------------------------------------------------------------
/src/vue/plugins/vuex.js:
--------------------------------------------------------------------------------
1 |
2 | // State management library
3 | // https://vuex.vuejs.org/en/
4 | // NOTE: This file is only initializes the plugin, the state management logic is under store/
5 |
6 | import Vue from 'vue';
7 | import Vuex from 'vuex';
8 |
9 | // Fetch the state, actions and getters etc. from under store/
10 | import { store } from '@store';
11 |
12 | // Autoload plugin
13 | Vue.use(Vuex);
14 |
15 | // Export a new plugin instance
16 | export default new Vuex.Store(store);
17 |
--------------------------------------------------------------------------------
/src/components/containers/PanelContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
29 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-position.scss:
--------------------------------------------------------------------------------
1 |
2 | .static {
3 | position: static;
4 | }
5 |
6 | .relative {
7 | position: relative;
8 | }
9 |
10 | .absolute {
11 | position: absolute;
12 | }
13 |
14 | .fixed {
15 | position: fixed;
16 | }
17 |
18 | .absolute,
19 | .fixed {
20 |
21 | &.keep-top {
22 | top: 0;
23 | }
24 |
25 | &.keep-bottom {
26 | bottom: 0;
27 | }
28 |
29 | &.keep-left {
30 | left: 0;
31 | }
32 |
33 | &.keep-right {
34 | right: 0;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/styles/transitions/transition-fade-in.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | // FIXME: would be better to not specify the opacity value 1, but let the default or component's defined value stand
5 | .transition-fade-in-enter-active {
6 | opacity: 1;
7 | @include transition-fast;
8 | @include transition-properties(opacity);
9 | }
10 |
11 | .transition-fade-in-leave-active {
12 | @include no-transition;
13 | }
14 |
15 | // Start state for enter
16 | .transition-fade-in-enter {
17 | opacity: 0;
18 | }
19 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-container.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | .container-white {
5 | @include container-white;
6 | }
7 |
8 | .container-white-buffer {
9 | @include container-white-buffer;
10 | }
11 |
12 | .container-dark {
13 | @include container-dark;
14 | }
15 |
16 | .container-dark-buffer {
17 | @include container-dark-buffer;
18 | }
19 |
20 | .container-card {
21 | @include container-card;
22 | }
23 |
24 | .container-card-buffer {
25 | @include container-card-buffer;
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/pages/PageNoAccess.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Go to dashboard
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible Node.js debug attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceRoot}/tooling/dev-server.js"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/src/models/User.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export default Vue.extend({
4 |
5 | props: {
6 |
7 | id: {
8 | type: Number,
9 | required: true
10 | },
11 |
12 | name: {
13 | type: String
14 | },
15 |
16 | email: {
17 | type: String
18 | },
19 |
20 | website: {
21 | type: String,
22 | default: null
23 | }
24 |
25 | },
26 |
27 | computed: {
28 |
29 | hasWebsite: function () {
30 | return this.website && this.website.length ? true : false;
31 | }
32 |
33 | }
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/src/util/getDomainName.js:
--------------------------------------------------------------------------------
1 | import { trim } from 'lodash';
2 |
3 | export default function (string) {
4 | var domain = trim(string);
5 |
6 | // Find & remove protocol (http, ftp, etc.) and get domain
7 | if (domain.indexOf('://') > -1) {
8 | domain = domain.split('/')[2];
9 | } else {
10 | domain = domain.split('/')[0];
11 | }
12 |
13 | // Find & remove query parameters
14 | domain = domain.split('?')[0];
15 |
16 | // Find & remove port number
17 | domain = domain.split(':')[0];
18 |
19 | return domain;
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/base/base-code.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | code {
7 | display: inline-block;
8 | }
9 |
10 | pre {
11 | code {
12 | // line-height: $line-height-tight;
13 |
14 | display: block;
15 | padding: 1.6em;
16 | // border-width: 1px;
17 |
18 | @include radius-loose;
19 | // border-color: $color-lightgrey;
20 | // background-color: $color-verylightgrey;
21 | // background-color: $color-white;
22 | color: $color-white;
23 | background-color: $color-darkgrey;
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/snippets/InlineIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
--------------------------------------------------------------------------------
/src/components/pages/PageOffline.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
--------------------------------------------------------------------------------
/src/locales/date-time-formats.js:
--------------------------------------------------------------------------------
1 |
2 | // NOTE: browser will handle the final part of localisation, e.g. month names etc.
3 | // See details: http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor
4 | export default {
5 |
6 | 'en': {
7 | short: {
8 | year: 'numeric',
9 | month: 'short',
10 | day: 'numeric'
11 | },
12 | long: {
13 | year: 'numeric',
14 | month: 'short',
15 | day: 'numeric',
16 | weekday: 'short',
17 | hour: 'numeric',
18 | minute: 'numeric'
19 | }
20 | }
21 |
22 | };
23 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // https: //code.visualstudio.com/docs/editor/tasks
3 | "version": "0.1.0",
4 | "tasks": [
5 |
6 | // // Show all problems in the project
7 | // {
8 | // "taskName": "Show all problems in project",
9 | // "command": "eslint",
10 | // "isShellCommand": true,
11 | // "args": [
12 | // "-w",
13 | // "-p",
14 | // ".",
15 | // "--noEmit"
16 | // ],
17 | // "showOutput": "silent",
18 | // "isBackground": true,
19 | // "problemMatcher": "$eslint-stylish"
20 | // }
21 |
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/src/models/Comment.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export default Vue.extend({
4 |
5 | props: {
6 |
7 | id: {
8 | type: Number,
9 | required: true
10 | },
11 |
12 | postId: {
13 | type: Number,
14 | required: true
15 | },
16 |
17 | name: {
18 | type: String
19 | },
20 |
21 | email: {
22 | type: String
23 | },
24 |
25 | body: {
26 | type: String
27 | }
28 |
29 | },
30 |
31 | computed: {
32 |
33 | excerpt: function () {
34 | return this.body.substr(0, 100);
35 | }
36 |
37 | }
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/tooling/e2e/nightwatch.globals.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var HtmlReporter = require('nightwatch-html-reporter')
3 |
4 | var reportsDirectory = path.join(__dirname, '../../reports')
5 |
6 | var reporter = new HtmlReporter({
7 | openBrowser: false,
8 | reportsDirectory: reportsDirectory,
9 | reportFilename: 'e2e.html',
10 | relativeScreenshots: true,
11 |
12 | // https://github.com/jls/nightwatch-html-reporter/tree/master/lib/themes
13 | themeName: 'outlook'
14 | })
15 |
16 | module.exports = {
17 | reporter: reporter.fn
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/containers/Card.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-background.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | .background-contain {
5 | @include background-contain;
6 | }
7 |
8 | .background-cover {
9 | @include background-cover;
10 | }
11 |
12 |
13 |
14 | .background-fill-height {
15 | @include background-fill-height;
16 | }
17 |
18 | .background-fill-width {
19 | @include background-fill-width;
20 | }
21 |
22 |
23 |
24 | .no-background-color {
25 | @include no-background-color;
26 | }
27 |
28 | .no-background-image {
29 | @include no-background-image;
30 | }
31 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-limit.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .limit-max {
7 | max-width: 100%;
8 | }
9 |
10 | .limit-tiny {
11 | @include limit-tiny;
12 | }
13 |
14 | .limit-small {
15 | @include limit-small;
16 | }
17 |
18 | .limit-smallish {
19 | @include limit-smallish;
20 | }
21 |
22 | .limit-medium {
23 | @include limit-medium;
24 | }
25 |
26 | .limit-large {
27 | @include limit-large;
28 | }
29 |
30 | .limit-verylarge {
31 | @include limit-verylarge;
32 | }
33 |
34 | .no-limit {
35 | @include no-limit;
36 | }
37 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-keep.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Regular horizontal stuff
7 |
8 | .keep-left {
9 | @include keep-left;
10 | }
11 |
12 | .keep-right {
13 | @include keep-right;
14 | }
15 |
16 | .keep-center {
17 | @include keep-center;
18 | }
19 |
20 |
21 |
22 | // Advanced positioning
23 |
24 | .keep-vertical-center {
25 | @include keep-vertical-center;
26 | }
27 |
28 | .keep-horizontal-center {
29 | @include keep-horizontal-center;
30 | }
31 |
32 | .keep-full-center {
33 | @include keep-full-center;
34 | }
35 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-rhythm.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './mixin-push';
3 |
4 |
5 |
6 | @mixin no-rhythm {
7 |
8 | object,
9 | iframe,
10 | h1,
11 | h2,
12 | h3,
13 | h4,
14 | h5,
15 | h6,
16 | p,
17 | blockquote,
18 | pre,
19 | abbr,
20 | address,
21 | q,
22 | dl,
23 | dt,
24 | dd,
25 | ol,
26 | ul,
27 | li,
28 | table,
29 | caption,
30 | tbody,
31 | tfoot,
32 | thead,
33 | tr,
34 | th,
35 | td,
36 | article,
37 | aside,
38 | footer,
39 | header,
40 | hgroup,
41 | menu,
42 | nav,
43 | section {
44 | @include no-push-vertical;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/spec/components/pages/PageHome.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import PageHome from '@vue-components/pages/PageHome';
3 |
4 | describe('PageHome.vue', function () {
5 |
6 | it('should render correct page title', function () {
7 |
8 | // Set up a new instance of the component
9 | const Constructor = Vue.extend(PageHome);
10 | const vm = new Constructor().$mount();
11 |
12 | // Expected results
13 | // console.log('PageHome.spec', vm.$el.querySelector('h1'));
14 | expect(vm.$el.querySelector('h1').textContent)
15 | .to.equal('Hello World!');
16 |
17 | });
18 |
19 | });
20 |
--------------------------------------------------------------------------------
/src/config/config.styles.js:
--------------------------------------------------------------------------------
1 | import { includes } from 'lodash';
2 |
3 | // Get SCSS constants
4 | // eslint-disable-next-line import/no-webpack-loader-syntax
5 | import styles from '!sass-to-js-var-loader!@styles/definitions/constants';
6 |
7 | // Convert some values to numbers
8 | var parsedStyles = {};
9 | for (var key in styles) {
10 | var potentialUnit = styles[key].substr(-2);
11 | if (includes(['ms', 'px'], potentialUnit)) {
12 | parsedStyles[key] = parseInt(styles[key]);
13 | } else {
14 | parsedStyles[key] = styles[key];
15 | }
16 | }
17 |
18 | export default parsedStyles;
19 |
--------------------------------------------------------------------------------
/src/components/pages/PageLogin.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Log in
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/src/locales/en.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | // Titlebar titles
5 | // NOTE: each named route should have a corresponding title here
6 | pageTitles: {
7 |
8 | // Top-level pages
9 | home: 'Home',
10 | posts: 'Posts',
11 | post: '@:pageTitles.posts',
12 |
13 | // Console sub pages
14 | console: 'Console',
15 | consoleComponents: '@:pageTitles.console',
16 | consoleConfiguration: '@:pageTitles.console',
17 | consoleModels: '@:pageTitles.console',
18 | consolePlugins: '@:pageTitles.console',
19 | consoleServices: '@:pageTitles.console',
20 | consoleVuex: '@:pageTitles.console'
21 |
22 | }
23 |
24 | };
25 |
--------------------------------------------------------------------------------
/src/util/linkIsExternal.js:
--------------------------------------------------------------------------------
1 | import eventHasMetaKey from './eventHasMetaKey';
2 |
3 | // Determine if a link element should be considered external or internal
4 | export default function (element) {
5 | if (
6 |
7 | // Is link
8 | element.href &&
9 |
10 | // Is external
11 | (element.hostname !== location.hostname) &&
12 |
13 | // Not using meta key
14 | !eventHasMetaKey(event) &&
15 |
16 | // No target specified
17 | (!element.target || element.target === '') &&
18 |
19 | // Is an http(s) link
20 | (element.protocol.substr(0, 4) === 'http')
21 |
22 | ) {
23 | return true;
24 | }
25 |
26 | return false;
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-keep.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin keep-left {
3 | clear: none;
4 | float: left;
5 | }
6 |
7 | @mixin keep-right {
8 | clear: none;
9 | float: right;
10 | }
11 |
12 | @mixin keep-center {
13 | margin-left: auto;
14 | margin-right: auto;
15 | }
16 |
17 | @mixin keep-vertical-center {
18 | top: 50%;
19 | transform: translateY(-50%);
20 | }
21 |
22 | @mixin keep-horizontal-center {
23 | left: 50%;
24 | transform: translateX(-50%);
25 | }
26 |
27 | @mixin keep-full-center ($extra-transform-values...) {
28 | position: absolute;
29 | top: 50%;
30 | left: 50%;
31 | transform: translate3d(-50%, -50%, 0) $extra-transform-values;
32 | }
33 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | import { merge } from 'lodash';
2 |
3 | // Actual config files
4 | // import defaultAliases from './config.aliases';
5 | import defaultBase from './config.base';
6 | import defaultStyles from './config.styles';
7 |
8 | // Dev overrides
9 | import devBase from './config.dev.base';
10 |
11 | // Merge configs with multiple sources
12 | let mergedBase = defaultBase;
13 |
14 | if (process.env.NODE_ENV === 'development') {
15 | mergedBase = merge(mergedBase, devBase);
16 | }
17 |
18 | mergedBase.styles = defaultStyles;
19 |
20 | // Export base config with aliases, routes and styles added as separate items
21 | export default mergedBase;
22 |
--------------------------------------------------------------------------------
/src/services/panels.js:
--------------------------------------------------------------------------------
1 | import { kebabCase } from 'lodash';
2 | import Vue from 'vue';
3 |
4 | export default new Vue({
5 |
6 | data: function () {
7 | return {
8 | component: null
9 | };
10 | },
11 |
12 | computed: {
13 |
14 | shouldBeShown: function () {
15 | return this.component ? true : false;
16 | }
17 |
18 | },
19 |
20 | methods: {
21 |
22 | open: function (component) {
23 | if (component) {
24 | this.component = kebabCase(component);
25 | }
26 | },
27 |
28 | close: function () {
29 | this.component = null;
30 | }
31 |
32 | },
33 |
34 | created: function () {
35 | window.b = this;
36 | }
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/src/models/index.js:
--------------------------------------------------------------------------------
1 | // All models
2 | import Account from './Account';
3 | import Comment from './Comment';
4 | import Post from './Post';
5 | import Remote from './Remote';
6 | import User from './User';
7 |
8 | // Helpers
9 | // Allow initialising a model without having to know Vue's syntax for passing property values
10 | const init = function (Model, data) {
11 | return new Model({
12 | propsData: data
13 | });
14 | };
15 |
16 | // Exports
17 |
18 | export {
19 | init,
20 | Account,
21 | Comment,
22 | Post,
23 | Remote,
24 | User
25 | };
26 |
27 | export default {
28 | init,
29 | Account,
30 | Comment,
31 | Post,
32 | Remote,
33 | User
34 | };
35 |
--------------------------------------------------------------------------------
/src/styles/utilities-composed/utility-inverse.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Text context
7 |
8 | // This is used for rich body text. Elements commonly used in building components, like links, are robbed of their sensible defaults. Attach this utility class on a body text container to style the rich text elements in a way that makes sense for natural article content.
9 |
10 | .inverse {
11 | color: $color-dark;
12 |
13 | // Light background buttons
14 | .button,
15 | .button-stroke,
16 | .button-plain {
17 |
18 | // Text color
19 | &:active {
20 | background-color: rgba($color-dark, 0.05);
21 | }
22 |
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/styles/base/base-tables.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | table {
5 |
6 | &.pad,
7 | &.separate,
8 | &.separate-bottom {
9 |
10 | td,
11 | th {
12 | @include pad-loose;
13 | }
14 |
15 | }
16 |
17 | &.pad {
18 | padding: 0;
19 | }
20 |
21 | &.separate {
22 |
23 | td,
24 | th {
25 | border-width: 1px;
26 | }
27 |
28 | }
29 |
30 | &.separate-bottom {
31 |
32 | td,
33 | th {
34 | border-bottom-width: 1px;
35 | }
36 |
37 | tr {
38 | &:last-child {
39 | td,
40 | th {
41 | border-bottom-width: 0;
42 | }
43 | }
44 | }
45 |
46 | }
47 |
48 | }
49 |
50 | th,
51 | td {
52 | padding: 0;
53 | }
54 |
--------------------------------------------------------------------------------
/tooling/unit/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
5 | // require all test files (files that ends with .spec.js)
6 | // const testsContext = require.context('@spec-unit', true, /\.spec$/)
7 | const testsContext = require.context('@spec', true, /^(?!e2e).*\.spec/gm)
8 | testsContext.keys().forEach(testsContext)
9 |
10 | // require all src files except main.js for coverage.
11 | // you can also change this to match only the subset of files that
12 | // you want coverage for.
13 |
14 | // /^\.\/(?!main(\.js)?$)(?!.*\.scss$)/
15 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
16 | srcContext.keys().forEach(srcContext)
17 |
--------------------------------------------------------------------------------
/spec/e2e/appContentVisibleAtStart.spec.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 |
6 | 'App wrapper should be visible at start': function (browser) {
7 |
8 | // automatically uses dev Server port from `/tooling/env/index.js`
9 | // default: http://localhost:8080
10 | // see nightwatch.conf.js
11 | const devServer = browser.globals.devServerURL;
12 |
13 | browser
14 | .url(devServer)
15 | .waitForElementVisible('.view-app', 5000)
16 | .assert.elementPresent('.view-app-content')
17 | // .assert.elementPresent('.view-page-log-in-actions .view-click-button')
18 | .end();
19 |
20 | }
21 |
22 | };
23 |
--------------------------------------------------------------------------------
/src/styles/transitions/transition-fade.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Transition code
7 | // https://vuejs.org/v2/guide/transitions.html
8 | // https://vuejs.org/images/transition.png
9 |
10 | // While either transition is playing
11 | // FIXME: would be better to not specify the opacity value 1, but let the default or component's defined value stand
12 | .transition-fade-enter-active,
13 | .transition-fade-leave-active {
14 | opacity: 1;
15 | @include transition-fast;
16 | @include transition-properties(opacity);
17 | }
18 |
19 | // Start state for enter
20 | .transition-fade-enter,
21 |
22 | // End state for exit
23 | .transition-fade-leave-to {
24 | opacity: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/index.js:
--------------------------------------------------------------------------------
1 |
2 | import api from './api';
3 | import auth from './auth';
4 | import env from './env';
5 | import menu from './menu';
6 | import network from './network';
7 | import notifications from './notifications';
8 | import panels from './panels';
9 | import popovers from './popovers';
10 | import time from './time';
11 | import viewport from './viewport';
12 |
13 | export {
14 | api,
15 | auth,
16 | env,
17 | menu,
18 | network,
19 | notifications,
20 | panels,
21 | popovers,
22 | time,
23 | viewport
24 | };
25 |
26 | export default {
27 | api,
28 | auth,
29 | env,
30 | menu,
31 | network,
32 | notifications,
33 | panels,
34 | popovers,
35 | time,
36 | viewport
37 | };
38 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-box-model.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .content-box {
7 | @include content-box;
8 | }
9 |
10 | .border-box {
11 | @include border-box;
12 | }
13 |
14 |
15 |
16 | .border-collapse {
17 | @include border-collapse;
18 | }
19 |
20 | .no-border-collapse {
21 | @include no-border-collapse;
22 | }
23 |
24 |
25 |
26 | .background-clip-content-box {
27 | @include background-clip-content-box;
28 | }
29 |
30 | .background-clip-border-box {
31 | @include background-clip-border-box;
32 | }
33 |
34 | .background-clip-padding-box {
35 | @include background-clip-padding-box;
36 | }
37 |
38 | .background-clip-text {
39 | @include background-clip-text;
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/console/ConsoleConfiguration.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 | {{ key }}
24 | {{ JSON.stringify(value, null, 2) }}
25 |
26 |
27 |
28 |
29 |
30 |
34 |
--------------------------------------------------------------------------------
/src/components/popovers/PopoverCounter.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
Vuex counter value: {{ valueToShow }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/src/svg/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/check
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/sections/FooterSummary.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
40 |
--------------------------------------------------------------------------------
/src/services/api/apiAuth.js:
--------------------------------------------------------------------------------
1 | // NOTE: this only provides mock API functionality, as you can see below
2 |
3 | import { Account } from '@models';
4 |
5 | export default {
6 |
7 | methods: {
8 |
9 | transformAuth: function (user) {
10 | return {
11 | expires: null,
12 | account: new Account({
13 | propsData: {
14 | id: user.id,
15 | email: user.email,
16 | name: user.name,
17 | role: 'loggedUser'
18 | }
19 | })
20 | };
21 | },
22 |
23 | getAuth: function (onSuccess, onFail, onEither) {
24 | return this.get(
25 | 'users/1',
26 | {},
27 | onSuccess,
28 | onFail,
29 | onEither,
30 | this.transformAuth
31 | );
32 | }
33 |
34 | }
35 |
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/containers/Panel.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
43 |
--------------------------------------------------------------------------------
/src/svg/chevron-up.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-up
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/svg/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-down
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/svg/chevron-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-left
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/svg/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-right
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/styles/keyframes/keyframes-pulse.scss:
--------------------------------------------------------------------------------
1 |
2 | @keyframes pulse {
3 |
4 | 0%,
5 | 100% {
6 | opacity: 1;
7 | }
8 |
9 | 50% {
10 | opacity: 0;
11 | }
12 |
13 | }
14 |
15 | @keyframes pulse-delayed {
16 |
17 | 0%,
18 | 40%,
19 | 100% {
20 | opacity: 1;
21 | }
22 |
23 | 20% {
24 | opacity: 0;
25 | }
26 |
27 | }
28 |
29 | @keyframes pulse-scale {
30 |
31 | 0%,
32 | 100% {
33 | opacity: 1;
34 | transform: scale(1);
35 | }
36 |
37 | 50% {
38 | opacity: 0;
39 | transform: scale(0);
40 | }
41 |
42 | }
43 |
44 | @keyframes pulse-scale-delayed {
45 |
46 | 0%,
47 | 40%,
48 | 100% {
49 | opacity: 1;
50 | transform: scale(1);
51 | }
52 |
53 | 20% {
54 | opacity: 0;
55 | transform: scale(0);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | 'rules': {
20 | // allow paren-less arrow functions
21 | 'arrow-parens': 0,
22 | // allow async-await
23 | 'generator-star-spacing': 0,
24 | // allow debugger during development
25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/svg/cross.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/cross
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/svg/hamburger.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/hamburger
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-shadow.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin shadow ($opacity: 0.15, $color: $color-darkgrey) {
3 | box-shadow: 0 2px 8px rgba($color, $opacity);
4 | }
5 |
6 | @mixin shadow-transparent ($color: $color-darkgrey) {
7 | @include shadow(0, $color);
8 | }
9 |
10 |
11 |
12 | @mixin shadow-tight ($opacity: 0.15, $color: $color-darkgrey) {
13 | box-shadow: 0 1px 2px rgba($color, $opacity);
14 | }
15 |
16 | @mixin shadow-tight-transparent ($color: $color-darkgrey) {
17 | @include shadow-tight(0, $color);
18 | }
19 |
20 |
21 |
22 | @mixin shadow-loose ($opacity: 0.15, $color: $color-darkgrey) {
23 | box-shadow: 0 2px 38px rgba($color, $opacity);
24 | }
25 |
26 | @mixin shadow-loose-transparent ($color: $color-darkgrey) {
27 | @include shadow-loose(0, $color);
28 | }
29 |
--------------------------------------------------------------------------------
/src/services/notifications.js:
--------------------------------------------------------------------------------
1 |
2 | import { debounce } from 'lodash';
3 | import Vue from 'vue';
4 |
5 | const delay = 6 * 1000;
6 |
7 | export default new Vue({
8 |
9 | data: function () {
10 | return {
11 | message: null
12 | };
13 | },
14 |
15 | computed: {
16 |
17 | shouldBeShown: function () {
18 | return this.message ? true : false;
19 | }
20 |
21 | },
22 |
23 | methods: {
24 |
25 | show: function (message) {
26 | if (message) {
27 | this.message = message;
28 | }
29 | },
30 |
31 | close: function () {
32 | if (this.message !== null) {
33 | this.message = null;
34 | }
35 | }
36 |
37 | },
38 |
39 | watch: {
40 |
41 | message: debounce(function () {
42 | this.close();
43 | }, delay)
44 |
45 | }
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/services/api/apiPosts.js:
--------------------------------------------------------------------------------
1 | import { Post } from '@models';
2 |
3 | export default {
4 |
5 | // computed: {},
6 |
7 | methods: {
8 |
9 | // Return a new model instance instead of the raw data
10 | transformPost: function (postData) {
11 | return new Post({
12 | propsData: postData
13 | });
14 | },
15 |
16 | getPosts: function (onSuccess, onFail, onEither) {
17 | return this.get(
18 | 'posts',
19 | {},
20 | onSuccess,
21 | onFail,
22 | onEither,
23 | this.transformPost
24 | );
25 | },
26 |
27 | getPost: function (id, onSuccess, onFail, onEither) {
28 | return this.get(
29 | 'posts/' + id,
30 | {},
31 | onSuccess,
32 | onFail,
33 | onEither,
34 | this.transformPost
35 | );
36 | }
37 |
38 | }
39 |
40 | };
41 |
--------------------------------------------------------------------------------
/src/services/api/apiUsers.js:
--------------------------------------------------------------------------------
1 | import { User } from '@models';
2 |
3 | export default {
4 |
5 | // computed: {},
6 |
7 | methods: {
8 |
9 | // Return a new model instance instead of the raw data
10 | transformUser: function (userData) {
11 | return new User({
12 | propsData: userData
13 | });
14 | },
15 |
16 | getUsers: function (onSuccess, onFail, onEither) {
17 | return this.get(
18 | 'users',
19 | {},
20 | onSuccess,
21 | onFail,
22 | onEither,
23 | this.transformUser
24 | );
25 | },
26 |
27 | getUser: function (id, onSuccess, onFail, onEither) {
28 | return this.get(
29 | 'users/' + id,
30 | {},
31 | onSuccess,
32 | onFail,
33 | onEither,
34 | this.transformUser
35 | );
36 | }
37 |
38 | }
39 |
40 | };
41 |
--------------------------------------------------------------------------------
/src/components/pages/PageNotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
25 |
26 |
27 | Back to home page
28 |
29 |
30 |
31 |
32 |
33 |
40 |
--------------------------------------------------------------------------------
/src/svg/chevron-left-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-left-left
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/svg/chevron-right-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/chevron-right-right
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tooling/e2e/nightwatch.chrome.conf.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash')
2 | var path = require('path')
3 | require('babel-register')
4 |
5 | var defaults = require('./nightwatch.conf.js')
6 |
7 | module.exports = _.merge(defaults, {
8 | globals_path: path.join(__dirname, './nightwatch.chrome.globals.js'),
9 |
10 | selenium: {
11 | start_process: false
12 | },
13 |
14 | test_settings: {
15 | default: {
16 | selenium_port: 9515,
17 | default_path_prefix: '',
18 |
19 | desiredCapabilities: {
20 | browserName: 'chrome',
21 | acceptSslCerts: true,
22 |
23 | // https://sites.google.com/a/chromium.org/chromedriver/capabilities#TOC-chromeOptions-object
24 | chromeOptions: {
25 | args: ['--no-sandbox']
26 | }
27 |
28 | }
29 |
30 | }
31 | }
32 |
33 | })
34 |
--------------------------------------------------------------------------------
/src/styles/transitions/transition-flip-vertical.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | // Shared, defaults
5 | .transition-flip-vertical-enter-active,
6 | .transition-flip-vertical-leave-active {
7 | transition-property: transform;
8 | }
9 |
10 | .transition-flip-vertical-enter-active {
11 | @include transition-veryfast;
12 | }
13 |
14 | .transition-flip-vertical-leave-active {
15 | @include transition-veryfast;
16 | }
17 |
18 | // Default state to transition a) to when entering and b) from when leaving
19 | .transition-flip-vertical-enter-active,
20 | .transition-flip-vertical-leave-active {
21 | transform: rotateX(0deg);
22 | }
23 |
24 | // Start state when entering
25 | .transition-flip-vertical-enter {
26 | transform: rotateX(-90deg);
27 | }
28 |
29 | // Out
30 | .transition-flip-vertical-leave-to {
31 | transform: rotateX(90deg);
32 | }
33 |
--------------------------------------------------------------------------------
/src/styles/utilities-composed/utility-control.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | .control,
5 | .control-area,
6 | .control-scale {
7 | @include transition-hover-active;
8 | @include transition-properties-common;
9 | }
10 |
11 | // Default feedback for hit areas
12 | // NOTE: might be better not to set anything here since this might need a lot of overriding in many places
13 | .control-enabled {
14 | @include cursor-pointer;
15 |
16 | &.control-area {
17 | &:active {
18 | background-color: $color-feedback-dark;
19 | }
20 | }
21 |
22 | &.control-scale {
23 | &:active {
24 | transform: scale($scale-small);
25 | }
26 | }
27 |
28 | }
29 |
30 | // .control-disabled {}
31 |
32 | // Prevent user select when double clicking or swiping etc.
33 | .control-enabled {
34 | &.control-mouse-down {
35 | user-select: none;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/vue/plugins/vue-i18n.js:
--------------------------------------------------------------------------------
1 |
2 | // vue-i18n: Internationalization for Vue
3 | // http://kazupon.github.io/vue-i18n/en/installation.html
4 | import Vue from 'vue';
5 | import VueI18n from 'vue-i18n';
6 |
7 | // Import route components for vue-router
8 | import config from '@config';
9 | import locales from '@locales';
10 |
11 |
12 |
13 | // Autoload plugin
14 | Vue.use(VueI18n);
15 |
16 | // Provide options for the plugin constructor (see docs for details)
17 | const options = {
18 |
19 | // Set default locale
20 | locale: config.defaultLocale,
21 | fallbackLocale: config.fallbackLocale,
22 |
23 | // Inject copy text, formats etc.
24 | messages: locales.messages,
25 | dateTimeFormats: locales.dateTimeFormats,
26 | numberFormats: locales.numberFormats
27 |
28 | };
29 |
30 | // Export a new plugin instance
31 | export default new VueI18n(options);
32 |
--------------------------------------------------------------------------------
/src/styles/transitions/transition-hide.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | .transition-hide-up-enter-active,
5 | .transition-hide-up-leave-active,
6 | .transition-hide-right-enter-active,
7 | .transition-hide-right-leave-active {
8 | @include transition-slow;
9 | @include transition-properties(transform);
10 | @include no-translate;
11 | }
12 |
13 | // Open by moving down, close by moving up
14 |
15 | // .transition-hide-up-enter-active,
16 | // .transition-hide-up-leave-active {}
17 |
18 | .transition-hide-up-enter,
19 | .transition-hide-up-leave-to {
20 | @include translate-up;
21 | }
22 |
23 |
24 |
25 | // Open from right to left, close to the right
26 |
27 | // .transition-hide-right-enter-active,
28 | // .transition-hide-right-leave-active {}
29 |
30 | .transition-hide-right-enter,
31 | .transition-hide-right-leave-to {
32 | @include translate-right;
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/models/PostListItem.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{ post.title }}
26 |
27 |
28 |
29 | {{ post.excerpt }}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
50 |
--------------------------------------------------------------------------------
/src/svg/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/arrow-left
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-separate.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Single
7 |
8 | .separate {
9 | border-width: $separator;
10 | }
11 |
12 | .separate-top {
13 | border-top-width: $separator;
14 | }
15 |
16 | .separate-right {
17 | border-right-width: $separator;
18 | }
19 |
20 | .separate-bottom {
21 | border-bottom-width: $separator;
22 | }
23 |
24 | .separate-left {
25 | border-left-width: $separator;
26 | }
27 |
28 |
29 |
30 | // Double
31 |
32 | .separate-double {
33 | border-width: $separator-double;
34 | }
35 |
36 | .separate-double-top {
37 | border-top-width: $separator-double;
38 | }
39 |
40 | .separate-double-right {
41 | border-right-width: $separator-double;
42 | }
43 |
44 | .separate-double-bottom {
45 | border-bottom-width: $separator-double;
46 | }
47 |
48 | .separate-double-left {
49 | border-left-width: $separator-double;
50 | }
51 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-container.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './mixin-buffer';
3 | @import './mixin-radius';
4 | @import './mixin-shadow';
5 |
6 | @mixin container-white {
7 | @include radius-loose;
8 | @include background-clip-padding-box;
9 | overflow: auto;
10 | border-width: 1px;
11 | border-color: $color-dark-translucent;
12 | background-color: $color-white;
13 | }
14 |
15 | @mixin container-white-buffer {
16 | @include container-white;
17 | @include buffer;
18 | }
19 |
20 | @mixin container-dark {
21 | @include radius;
22 | overflow: auto;
23 | background-color: color-translucent($color-dark, 0.05);
24 | }
25 |
26 | @mixin container-dark-buffer {
27 | @include container-dark;
28 | @include buffer;
29 | }
30 |
31 | @mixin container-card {
32 | @include container-white;
33 | @include shadow;
34 | }
35 |
36 | @mixin container-card-buffer {
37 | @include container-card;
38 | @include buffer;
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/controls/IconButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 |
51 |
--------------------------------------------------------------------------------
/tooling/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // the name of the method is the filename.
3 | // can be used in tests like this:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // for how to write custom assertions see
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 | exports.assertion = function (selector, count) {
10 | this.message = 'Testing if element <' + selector + '> has count: ' + count
11 | this.expected = count
12 | this.pass = function (val) {
13 | return val === this.expected
14 | }
15 | this.value = function (res) {
16 | return res.value
17 | }
18 | this.command = function (cb) {
19 | var self = this
20 | return this.api.execute(function (selector) {
21 | return document.querySelectorAll(selector).length
22 | }, [selector], function (res) {
23 | cb.call(self, res)
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/console/ConsoleModels.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Post 1
29 |
30 | id: {{ testPost.id }}
31 | name: {{ testPost.name }}
32 | isTopLevel: {{ testPost.isTopLevel }}
33 | hasParent: {{ testPost.hasParent }}
34 | hasChildren: {{ testPost.hasChildren }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-font.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin font-face ($font-family, $font-weight, $font-style, $filepath, $woffdata: null, $woff2data: null) {
3 |
4 | @font-face {
5 | font-family: $font-family;
6 | font-style: $font-style;
7 | font-weight: $font-weight;
8 | src: url('#{$filepath}.eot');
9 | }
10 |
11 | @font-face {
12 | font-family: $font-family;
13 | font-style: $font-style;
14 | font-weight: $font-weight;
15 |
16 | @if $woffdata {
17 | src:
18 | url('data:application/font-woff;charset=utf-8;base64,#{$woffdata}') format('woff'),
19 | url('data:application/font-woff2;charset=utf-8;base64,#{$woff2data}') format('woff2'),
20 | url('#{$filepath}.ttf') format('truetype');
21 | }
22 |
23 | @else {
24 | src:
25 | url('#{$filepath}.woff') format('woff'),
26 | url('#{$filepath}.woff2') format('woff2'),
27 | url('#{$filepath}.ttf') format('truetype');
28 | }
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/util/processLargeArray.js:
--------------------------------------------------------------------------------
1 | // Process large array in chunks so that UI isn't blocked for too long at once
2 | export default function (array, processItemCallback, finalCallback) {
3 | let chunk = 250;
4 | let i = 0;
5 |
6 | let doChunk = function (processItemCallback) {
7 | let count = chunk;
8 |
9 | // Process each item in this chunk
10 | while (count-- && i < array.length) {
11 |
12 | // Process array[i] here
13 | // NOTE: processItemCallback should not move, delete or add array items, but updating them is OK
14 | processItemCallback(array[i], i, array);
15 |
16 | // Iterate counter
17 | ++i;
18 | }
19 |
20 | // Continue loop in next chunk after allowing other tasks to go on
21 | if (i < array.length) {
22 | setTimeout(function () {
23 | doChunk(processItemCallback);
24 | }, 2);
25 | } else {
26 | finalCallback();
27 | }
28 |
29 | };
30 |
31 | doChunk(processItemCallback);
32 | }
33 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-background.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin background-contain {
3 | background-position: 50% 50%;
4 | background-size: contain;
5 | background-repeat: no-repeat;
6 | }
7 |
8 | @mixin background-cover {
9 | background-position: 50% 50%;
10 | background-size: cover;
11 | background-repeat: no-repeat;
12 | }
13 |
14 |
15 |
16 | @mixin background-fill-height {
17 | background-position: 0 50%;
18 | background-size: auto 100%;
19 | background-repeat: repeat-x;
20 | }
21 |
22 | @mixin background-fill-width {
23 | background-position: 50% 0;
24 | background-size: 100% auto;
25 | background-repeat: repeat-y;
26 | }
27 |
28 |
29 |
30 | // NOTE
31 | // - @mixin no-background is intentionally missing
32 | // - please use either `-color` or `-image` or both explicitly
33 |
34 | @mixin no-background-color {
35 | background-color: transparent;
36 | }
37 |
38 | @mixin no-background-image {
39 | background-image: none;
40 | }
41 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-fill.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .fill,
7 | .fill-relative,
8 | .fill-fixed,
9 | .fill-width,
10 | .fill-width-relative,
11 | .fill-width-fixed,
12 | .fill-height,
13 | .fill-height-relative,
14 | .fill-height-fixed {
15 | box-sizing: border-box;
16 | }
17 |
18 | .fill,
19 | .fill-relative,
20 | .fill-fixed,
21 | .fill-width,
22 | .fill-width-relative,
23 | .fill-width-fixed {
24 | left: 0;
25 | width: 100%;
26 | }
27 |
28 | .fill,
29 | .fill-relative,
30 | .fill-fixed,
31 | .fill-height,
32 | .fill-height-relative,
33 | .fill-height-fixed {
34 | top: 0;
35 | height: 100%;
36 | }
37 |
38 | .fill,
39 | .fill-width,
40 | .fill-height {
41 | position: absolute;
42 | }
43 |
44 | .fill-relative,
45 | .fill-width-relative,
46 | .fill-height-relative {
47 | position: relative;
48 | }
49 |
50 | .fill-fixed,
51 | .fill-width-fixed,
52 | .fill-height-fixed {
53 | position: fixed;
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/snippets/Pic.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
48 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-box-model.scss:
--------------------------------------------------------------------------------
1 |
2 | // Box sizing
3 |
4 | @mixin box-sizing ($value) {
5 | box-sizing: $value;
6 | }
7 |
8 | @mixin content-box {
9 | @include box-sizing(content-box);
10 | }
11 |
12 | @mixin border-box {
13 | @include box-sizing(border-box);
14 | }
15 |
16 |
17 |
18 | // Border-collapse
19 |
20 | @mixin border-collapse {
21 | border-collapse: collapse;
22 | }
23 |
24 | @mixin no-border-collapse {
25 | border-collapse: separate;
26 | }
27 |
28 |
29 |
30 | // Background-clip
31 |
32 | @mixin background-clip ($value) {
33 | background-clip: $value;
34 | }
35 |
36 | @mixin background-clip-content-box {
37 | @include background-clip(content-box);
38 | }
39 |
40 | @mixin background-clip-border-box {
41 | @include background-clip(border-box);
42 | }
43 |
44 | @mixin background-clip-padding-box {
45 | @include background-clip(padding-box);
46 | }
47 |
48 | @mixin background-clip-text {
49 | @include background-clip(text);
50 | }
51 |
--------------------------------------------------------------------------------
/tooling/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | // This is the webpack config used for unit tests.
2 |
3 | var utils = require('./utils')
4 | var webpack = require('webpack')
5 | var merge = require('webpack-merge')
6 | var baseConfig = require('./webpack.base.conf')
7 |
8 | var webpackConfig = merge(baseConfig, {
9 | // use inline sourcemap for karma-sourcemap-loader
10 | module: {
11 | rules: utils.styleLoaders()
12 | },
13 | devtool: '#inline-source-map',
14 | resolveLoader: {
15 | alias: {
16 | // necessary to to make lang="scss" work in test when using vue-loader's inject option
17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
18 | 'scss-loader': 'sass-loader'
19 | }
20 | },
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': require('./env/test.env')
24 | })
25 | ]
26 | })
27 |
28 | // no need for app entry during tests
29 | delete webpackConfig.entry
30 |
31 | module.exports = webpackConfig
32 |
--------------------------------------------------------------------------------
/src/svg/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/arrow-right
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/models/PostList.vue:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
27 |
42 |
43 |
44 |
45 |
59 |
--------------------------------------------------------------------------------
/src/components/panels/PanelReadme.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | What's new
26 |
27 | This and that
28 | You can now do this and that with this app. Previously you couldn't. We hope you're happy now!
29 |
30 |
31 |
32 |
33 |
34 | Debugging
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
50 |
--------------------------------------------------------------------------------
/src/models/Remote.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const pathValidator = function (path) {
4 | return typeof path === 'string';
5 | };
6 |
7 | export default Vue.extend({
8 |
9 | props: {
10 |
11 | path: {
12 | type: String,
13 | required: true,
14 | validator: pathValidator
15 | },
16 |
17 | api: {
18 | type: String,
19 | required: true,
20 | validator: pathValidator
21 | },
22 |
23 | login: {
24 | type: String,
25 | required: true,
26 | validator: pathValidator
27 | },
28 |
29 | logout: {
30 | type: String,
31 | required: true,
32 | validator: pathValidator
33 | }
34 |
35 | },
36 |
37 | computed: {
38 |
39 | basePath: function () {
40 | return this.path;
41 | },
42 |
43 | apiPath: function () {
44 | return this.basePath + this.api;
45 | },
46 |
47 | loginPath: function () {
48 | return this.basePath + this.login;
49 | },
50 |
51 | logoutPath: function () {
52 | return this.basePath + this.logout;
53 | }
54 |
55 | }
56 |
57 | });
58 |
--------------------------------------------------------------------------------
/src/services/api/apiComments.js:
--------------------------------------------------------------------------------
1 | import { Comment } from '@models';
2 |
3 | export default {
4 |
5 | // computed: {},
6 |
7 | methods: {
8 |
9 | transformComment: function (postData) {
10 | return new Comment({
11 | propsData: postData
12 | });
13 | },
14 |
15 | getComments: function (onSuccess, onFail, onEither) {
16 | return this.get(
17 | 'comments',
18 | {},
19 | onSuccess,
20 | onFail,
21 | onEither,
22 | this.transformComment
23 | );
24 | },
25 |
26 | getCommentsForPost: function (postId, onSuccess, onFail, onEither) {
27 | return this.get(
28 | 'comments',
29 | {
30 | postId: postId
31 | },
32 | onSuccess,
33 | onFail,
34 | onEither,
35 | this.transformComment
36 | );
37 | },
38 |
39 | getComment: function (id, onSuccess, onFail, onEither) {
40 | return this.get(
41 | 'comments/' + id,
42 | {},
43 | onSuccess,
44 | onFail,
45 | onEither,
46 | this.transformComment
47 | );
48 | }
49 |
50 | }
51 |
52 | };
53 |
--------------------------------------------------------------------------------
/spec/store/mutations/incrementCounter.spec.js:
--------------------------------------------------------------------------------
1 | import { mutations } from '@store';
2 |
3 | describe('mutations INCREMENT_COUNTER', function () {
4 |
5 | it('iterates from 0', function () {
6 |
7 | const mockState = {
8 | counter: 0
9 | };
10 |
11 | // Mutations take the state as argument
12 | mutations['INCREMENT_COUNTER'](mockState);
13 |
14 | // Expected result
15 | expect(mockState.counter).to.equal(1);
16 |
17 | });
18 |
19 | it('iterates from 1', function () {
20 | const mockState = { counter: 1 };
21 | mutations['INCREMENT_COUNTER'](mockState);
22 | expect(mockState.counter).to.equal(2);
23 | });
24 |
25 | it('iterates from -1', function () {
26 | const mockState = { counter: -1 };
27 | mutations['INCREMENT_COUNTER'](mockState);
28 | expect(mockState.counter).to.equal(0);
29 | });
30 |
31 | it('iterates from 999999', function () {
32 | const mockState = { counter: 999999 };
33 | mutations['INCREMENT_COUNTER'](mockState);
34 | expect(mockState.counter).to.equal(1000000);
35 | });
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-row.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './mixin-buffer';
3 | @import './mixin-clear';
4 | @import './mixin-keep';
5 | @import './mixin-viewport';
6 |
7 |
8 |
9 | // NOTE:
10 | // we haven't finalized the actual row-column system yet
11 | // this is temporarily empty, but a row is a valid level of abstraction
12 | // hence we want to allow other elements using this
13 |
14 | // stylelint-disable block-no-empty
15 | @mixin row {
16 | // @include clear-after;
17 | }
18 | // stylelint-enable block-no-empty
19 |
20 | @mixin row-content {
21 |
22 | // Base setup
23 | position: relative;
24 | @include keep-center;
25 | @include clear-after;
26 |
27 | // Width limits
28 | // @include limit-small;
29 | // @include limit-smallish;
30 | // @include limit-medium;
31 |
32 | // Basic dimensions
33 | @include buffer-even;
34 | @include viewport-over-tiny {
35 | @include buffer-loose-even;
36 | }
37 | // @include viewport-over-medium {
38 | // @include buffer-relative;
39 | // }
40 | // @include viewport-over-large {}
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/styles/base/base-rhythm.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | html {
7 | line-height: $line-height;
8 | }
9 |
10 | hr,
11 | p,
12 | ul,
13 | ol,
14 | table,
15 | pre,
16 | blockquote {
17 | @include push-vertical;
18 | }
19 |
20 | // Headings
21 | h1,
22 | h2,
23 | h3,
24 | h4,
25 | h5,
26 | h6 {
27 | @include push-tight-bottom;
28 | }
29 |
30 | // Normal top margin for this
31 | h1 {
32 | @include push-top;
33 | }
34 |
35 | // More top margin for these
36 | h2,
37 | h3,
38 | h4,
39 | h5,
40 | h6 {
41 | @include push-top-even;
42 | }
43 |
44 | // Tables
45 | caption {
46 | @include push-tight-bottom;
47 | }
48 |
49 | // Definition lists
50 | dl {
51 | @include push-bottom;
52 |
53 | // Utilities
54 | &.no-push,
55 | &.no-push-vertical,
56 | &.no-push-top,
57 | &.inline,
58 | &.inline-block {
59 | dt {
60 | @include no-push-top;
61 | }
62 | }
63 |
64 | }
65 |
66 | dt {
67 | @include push-top;
68 | @include no-push-bottom;
69 | }
70 |
71 | dd {
72 | margin-left: 0;
73 | @include no-push-vertical;
74 | }
75 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-animation.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin animation ($name, $duration: $transition-veryveryslow, $easing: null, $count: infinite, $direction: normal) {
3 | animation-name: $name;
4 | animation-duration: $duration;
5 | animation-iteration-count: $count;
6 | animation-direction: $direction;
7 |
8 | // Use explicitly defined easing
9 | @if $easing {
10 | animation-timing-function: $easing;
11 | }
12 |
13 | // Default to linear for infinite animations
14 | @else if $count == infinite {
15 | animation-timing-function: linear;
16 | }
17 |
18 | }
19 |
20 | @mixin animation-alternate ($name, $duration: $transition-veryveryslow, $easing: null, $count: infinite) {
21 | @include animation($name, $duration, $easing, $count, alternate);
22 | }
23 |
24 | @mixin animation-once ($name, $duration: $transition-veryveryslow, $easing: null) {
25 | @include animation($name, $duration, $easing, 1);
26 | }
27 |
28 | @mixin animation-pulse ($count: infinite) {
29 | @include animation(pulse, $transition-slow, $easing-smooth, $count, alternate);
30 | }
31 |
--------------------------------------------------------------------------------
/src/vue/plugins/index.js:
--------------------------------------------------------------------------------
1 |
2 | // Vue plugins
3 | // https://vuejs.org/v2/guide/plugins.html
4 | // https://eiskis.gitbooks.io/bellevue/app/vue.html
5 |
6 | // NOTE:
7 | // - These are integrated libraries that generally deliver new functionality to Vue components (or other objects)
8 | // - Generally they inject new functionality (such as this.$route) or read new values from the view model definition (such as metaInfo)
9 | // - When adding plugins, you need to look up the documentation for each to understand the correct way of loading them
10 | // - Each plugin is loaded via a separate file. You can import configuration from '@config' in each file.
11 | // - See documentation for more info on how to load various plugins.
12 |
13 | import VueI18n from './vue-i18n';
14 | import VueMeta from './vue-meta';
15 | import VueRouter from './vue-router';
16 | import Vuex from './vuex';
17 |
18 | export {
19 | VueI18n,
20 | VueMeta,
21 | VueRouter,
22 | Vuex
23 | };
24 |
25 | export default {
26 | VueI18n,
27 | VueMeta,
28 | VueRouter,
29 | Vuex
30 | };
31 |
--------------------------------------------------------------------------------
/src/styles/base/base-lists.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Lists
7 | ul,
8 | ol {
9 | padding-left: 0;
10 | list-style-type: none;
11 |
12 | ul,
13 | ol {
14 | margin-top: 0;
15 | margin-bottom: 0;
16 | }
17 |
18 | }
19 |
20 | // Inline lists
21 | // NOTE: use .inline.block to keep the list item itself block element
22 | ul,
23 | ol,
24 | dl {
25 |
26 | // Adjust behavior when combined with utility classes
27 |
28 | &.inline {
29 | li {
30 | display: inline;
31 | }
32 | }
33 |
34 | &.inline-block {
35 | li {
36 | display: inline-block;
37 | }
38 | }
39 |
40 |
41 |
42 | // Horizontal lists with floated block elements
43 | // FIXME: this should be a separate utility, now these are just magical class names
44 |
45 | &.collapse,
46 | &.collapse-right {
47 | clear: none;
48 | li,
49 | dt,
50 | dd {
51 | clear: none;
52 | float: left;
53 | }
54 | }
55 |
56 | &.collapse {
57 | @include clear-after;
58 | // float: left;
59 | }
60 | &.collapse-right {
61 | // float: right;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/snippets/InlineSpinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
33 |
34 |
35 |
36 |
37 |
38 |
58 |
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": false,
3 | "header-style": {
4 | "style": "atx"
5 | },
6 | "ul-style": {
7 | "style": "dash"
8 | },
9 | "list-indent": true,
10 | "ul-start-left": true,
11 | "ul-indent": {
12 | "indent": 1
13 | },
14 | "no-trailing-spaces": true,
15 | "no-hard-tabs": false,
16 | "no-reversed-links": true,
17 | "no-multiple-blanks": {
18 | "maximum": 3
19 | },
20 | "no-missing-space-atx": true,
21 | "no-multiple-space-atx": true,
22 | "no-missing-space-closed-atx": true,
23 | "no-multiple-space-closed-atx": true,
24 | "blanks-around-headers": true,
25 | "header-start-left": true,
26 | "no-multiple-space-blockquote": true,
27 | "no-blanks-blockquote": true,
28 | "ol-prefix": {
29 | "style": "ordered"
30 | },
31 | "list-marker-space": true,
32 | "blanks-around-fences": true,
33 | "blanks-around-lists": true,
34 | "no-inline-html": false,
35 | "hr-style": {
36 | "style": "---"
37 | },
38 | "no-emphasis-as-header": true,
39 | "no-space-in-emphasis": true,
40 | "no-space-in-code": true,
41 | "no-space-in-links": true,
42 | "no-empty-links": true
43 | }
44 |
--------------------------------------------------------------------------------
/tooling/build.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | var ora = require('ora')
6 | var rm = require('rimraf')
7 | var path = require('path')
8 | var chalk = require('chalk')
9 | var webpack = require('webpack')
10 | var config = require('./env')
11 | var webpackConfig = require('./webpack.prod.conf')
12 |
13 | var spinner = ora('building for production...')
14 | spinner.start()
15 |
16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
17 | if (err) throw err
18 | webpack(webpackConfig, function (err, stats) {
19 | spinner.stop()
20 | if (err) throw err
21 | process.stdout.write(stats.toString({
22 | colors: true,
23 | modules: false,
24 | children: false,
25 | chunks: false,
26 | chunkModules: false
27 | }) + '\n\n')
28 |
29 | console.log(chalk.cyan(' Build complete.\n'))
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ))
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/src/styles/transitions/transition-scale-fade.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 | // Shared, defaults
5 | .transition-scale-fade-enter,
6 | .transition-scale-fade-enter-active,
7 | .transition-scale-fade-leave,
8 | .transition-scale-fade-leave-active {
9 | transition-property: opacity, transform;
10 | }
11 |
12 | .transition-scale-fade-enter,
13 | .transition-scale-fade-enter-active {
14 | @include transition-fast;
15 | }
16 |
17 | .transition-scale-fade-leave,
18 | .transition-scale-fade-leave-active {
19 | @include transition-fast;
20 | }
21 |
22 | // Default state to transition a) to when entering and b) from when leaving
23 | .transition-scale-fade-enter-active,
24 | .transition-scale-fade-leave {
25 | opacity: 1;
26 | transform: scale(1);
27 | }
28 |
29 | // Start state when entering
30 | .transition-scale-fade-enter {
31 | opacity: 0;
32 | // transform: scale($scale-large);
33 | transform: scale($scale-verysmall);
34 | }
35 |
36 | // Out
37 | .transition-scale-fade-leave-active {
38 | opacity: 0;
39 | // transform: scale($scale-small);
40 | transform: scale($scale-verysmall);
41 | }
42 |
--------------------------------------------------------------------------------
/src/models/Account.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { includes } from 'lodash';
3 |
4 | import config from '@config';
5 |
6 | export default Vue.extend({
7 |
8 | props: {
9 |
10 | id: {
11 | type: Number,
12 | required: true
13 | },
14 |
15 | email: {
16 | type: String
17 | },
18 |
19 | name: {
20 | type: String
21 | },
22 |
23 | role: {
24 | type: String,
25 | default: null,
26 | validator: function (value) {
27 | return value === null || includes(config.routePermissionRoles, value);
28 | }
29 | },
30 |
31 | avatarUrl: {
32 | type: String,
33 | required: false,
34 | default: 'user-avatar-placeholder.png'
35 | }
36 |
37 | },
38 |
39 | computed: {
40 |
41 | nameOrEmail: function () {
42 | return this.name ? this.name : this.email;
43 | },
44 |
45 | accessLevel: function () {
46 | return config.routePermissionRoles.indexOf(this.role) + 1;
47 | },
48 |
49 | isAdmin: function () {
50 | let maxAccessLevel = config.routePermissionRoles.length;
51 | return this.accessLevel >= maxAccessLevel;
52 | }
53 |
54 | }
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Jerry Jäppinen
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 |
--------------------------------------------------------------------------------
/src/config/config.dev.base.js:
--------------------------------------------------------------------------------
1 | // Override base config values when in dev environment
2 | module.exports = {
3 | env: 'development',
4 |
5 | // Don't develop against production
6 | currentRemote: 'staging',
7 |
8 | // Additional remotes useful for development
9 | remotes: {
10 |
11 | localhost: {
12 | path: 'https://localhost/',
13 | api: '',
14 | login: 'login/',
15 | logout: 'logout/'
16 | },
17 |
18 | staging: {
19 | path: 'https://jsonplaceholder.typicode.com/',
20 | api: '',
21 | login: 'login/',
22 | logout: 'logout/'
23 | }
24 |
25 | },
26 |
27 | meta: {
28 | // title: 'DEVELOPMENT MODE'
29 | },
30 |
31 | // Turn off caching features for dev
32 | //
33 | // NOTE
34 | // There is a minor issue that might manifest itself if you change this:
35 | // https://github.com/Eiskis/bellevue/issues/28
36 | offlineCache: {
37 | enabled: false
38 | },
39 |
40 | // These routes are for dev only
41 | routePermissions: {
42 | 'console': 0,
43 | 'consoleComponents': 0,
44 | 'consoleConfiguration': 0,
45 | 'consoleModels': 0,
46 | 'consolePlugins': 0,
47 | 'consoleServices': 0,
48 | 'consoleVuex': 0
49 | }
50 |
51 | };
52 |
--------------------------------------------------------------------------------
/src/vue/plugins/vue-router.js:
--------------------------------------------------------------------------------
1 |
2 | // The officially supported router
3 | // https://router.vuejs.org/en/
4 | import Vue from 'vue';
5 | import Router from 'vue-router';
6 |
7 | // Import route components for vue-router
8 | import config from '@config';
9 |
10 | // eslint-disable-next-line import/extensions
11 | import defaultRoutes from '@config/config.routes';
12 |
13 | // eslint-disable-next-line import/extensions
14 | import devRoutes from '@config/config.dev.routes';
15 |
16 | // Merge configs with multiple sources
17 | let mergedRoutes = defaultRoutes;
18 |
19 | if (process.env.NODE_ENV === 'development') {
20 | mergedRoutes = mergedRoutes.concat(devRoutes);
21 | }
22 |
23 | // Autoload plugin
24 | Vue.use(Router);
25 |
26 | // Set up router options
27 | export const options = {
28 |
29 | // Class names used by
30 | // NOTE: these should conform to our class naming conventions
31 | linkActiveClass: config.router.linkActiveClass,
32 | linkExactActiveClass: config.router.linkExactActiveClass,
33 |
34 | // Our frontend URL scheme
35 | routes: mergedRoutes
36 |
37 | };
38 |
39 | // Export a new plugin instance
40 | export default new Router(options);
41 |
--------------------------------------------------------------------------------
/spec/services/env.spec.js:
--------------------------------------------------------------------------------
1 | import { env } from '@services';
2 |
3 | describe('Env service', function () {
4 |
5 | // https://stackoverflow.com/questions/19877924/what-is-the-list-of-possible-values-for-navigator-platform-as-of-today
6 | const n = {
7 | chromeMac: {
8 | ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
9 | platform: 'MacIntel'
10 | },
11 | safariMac: {
12 | ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4',
13 | platform: 'MacIntel'
14 | }
15 | };
16 |
17 | it('should return string for Mac on chromeMac', function () {
18 | var os = env.getOsFromNavigator(n.chromeMac.ua, n.chromeMac.platform);
19 | expect(os).to.be.a('string');
20 | });
21 |
22 | it('should detect Mac on chromeMac', function () {
23 | var os = env.getOsFromNavigator(n.chromeMac.ua, n.chromeMac.platform);
24 | expect(os).to.equal('mac');
25 | });
26 |
27 | it('should detect Mac on safariMac', function () {
28 | var os = env.getOsFromNavigator(n.safariMac.ua, n.safariMac.platform);
29 | expect(os).to.equal('mac');
30 | });
31 |
32 | });
33 |
--------------------------------------------------------------------------------
/src/util/index.js:
--------------------------------------------------------------------------------
1 | import clearSelection from './clearSelection';
2 | import composeClassnames from './composeClassnames';
3 | import escapeRegExpString from './escapeRegExpString';
4 | import eventHasMetaKey from './eventHasMetaKey';
5 | import extractClassnames from './extractClassnames';
6 | import linkIsExternal from './linkIsExternal';
7 | import getDomainName from './getDomainName';
8 | import startsWith from './startsWith';
9 | import processLargeArray from './processLargeArray';
10 | import replaceAll from './replaceAll';
11 | import scrollToElement from './scrollToElement';
12 | import trimWhitespace from './trimWhitespace';
13 |
14 | export {
15 | clearSelection,
16 | composeClassnames,
17 | escapeRegExpString,
18 | eventHasMetaKey,
19 | extractClassnames,
20 | linkIsExternal,
21 | getDomainName,
22 | startsWith,
23 | processLargeArray,
24 | replaceAll,
25 | scrollToElement,
26 | trimWhitespace
27 | };
28 |
29 | export default {
30 | clearSelection,
31 | composeClassnames,
32 | escapeRegExpString,
33 | eventHasMetaKey,
34 | extractClassnames,
35 | linkIsExternal,
36 | getDomainName,
37 | startsWith,
38 | processLargeArray,
39 | replaceAll,
40 | scrollToElement,
41 | trimWhitespace
42 | };
43 |
--------------------------------------------------------------------------------
/src/components/popovers/PopoverMainMenu.vue:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
45 |
46 |
47 |
48 |
57 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-type.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Font styles
7 | .type-uppercase {
8 | @include type-uppercase;
9 | }
10 |
11 | .type-small {
12 | @include type-small;
13 | }
14 |
15 | .type-large {
16 | @include type-large;
17 | }
18 |
19 | .type-mono {
20 | @include type-mono;
21 | }
22 |
23 | .type-display {
24 | @include type-display;
25 | }
26 |
27 | .type-serif {
28 | @include type-serif;
29 | }
30 |
31 | .type-large {
32 | @include type-large;
33 | }
34 |
35 |
36 |
37 | // Headings
38 | .type-h1 {
39 | @include type-h1;
40 | }
41 |
42 | .type-h2 {
43 | @include type-h2;
44 | }
45 |
46 | .type-h3 {
47 | @include type-h3;
48 | }
49 |
50 | .type-h4 {
51 | @include type-h4;
52 | }
53 |
54 | .type-h5 {
55 | @include type-h5;
56 | }
57 |
58 | .type-hyphens {
59 | @include type-hyphens;
60 | }
61 |
62 | .type-nohyphens {
63 | @include type-nohyphens;
64 | }
65 |
66 | .type-nobreak {
67 | @include type-nobreak;
68 | }
69 |
70 | .type-ellipsis {
71 | @include type-ellipsis;
72 | }
73 |
74 | .type-discreet {
75 | @include type-discreet;
76 | }
77 |
78 | .type-discreet-small {
79 | @include type-discreet-small;
80 | }
81 |
82 | .type-warning {
83 | @include type-warning;
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/snippets/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
67 |
--------------------------------------------------------------------------------
/src/vue/mixins/persist.js:
--------------------------------------------------------------------------------
1 | import { debounce } from 'lodash';
2 |
3 | // Set a computed property to automatically store in localStorage
4 | // https://vuejs.org/v2/guide/mixins.html
5 | export default {
6 |
7 | computed: {
8 |
9 | // NOTE: This can be undefined especially for non-components
10 | persistKey: function () {
11 | return this.$options.name;
12 | }
13 |
14 | },
15 |
16 | watch: {
17 |
18 | // Store serialized data into localStorage when it changes (throttled)
19 | persist: debounce(function (data) {
20 | if (this.persistKey) {
21 | localStorage.setItem(this.persistKey, JSON.stringify(data));
22 | }
23 | }, 500)
24 |
25 | },
26 |
27 | created: function () {
28 | if (this.persistKey && this.persist) {
29 |
30 | // Load serialized data from localStorage
31 | // NOTE: this is a synchronous operation, theoretically it might slow things down
32 | var data = localStorage.getItem(this.persistKey);
33 |
34 | if (data) {
35 | try {
36 | data = JSON.parse(data);
37 |
38 | // We found data in local storage, let's load it up
39 | if (data) {
40 | this.persist = data;
41 | }
42 |
43 | } catch (error) {
44 | console.error(error);
45 | }
46 | }
47 |
48 | }
49 |
50 | }
51 |
52 | };
53 |
--------------------------------------------------------------------------------
/src/components/snippets/Ellipsis.vue:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
29 | . . .
30 |
31 |
32 |
33 |
60 |
--------------------------------------------------------------------------------
/src/config/config.dev.routes.js:
--------------------------------------------------------------------------------
1 |
2 | // Adds new routes in addition to the defaults
3 | import components from '@vue-components';
4 |
5 | export default [
6 |
7 | {
8 | path: '/console',
9 | name: 'console',
10 | component: components.PageConsole,
11 |
12 | // Redirecting to the default child page
13 | redirect: {
14 | name: 'consoleComponents'
15 | },
16 |
17 | // https://router.vuejs.org/en/essentials/nested-routes.html
18 | children: [
19 |
20 | {
21 | path: 'components',
22 | name: 'consoleComponents',
23 | component: components.ConsoleComponents
24 | },
25 |
26 | {
27 | path: 'configuration',
28 | name: 'consoleConfiguration',
29 | component: components.ConsoleConfiguration
30 | },
31 |
32 | {
33 | path: 'models',
34 | name: 'consoleModels',
35 | component: components.ConsoleModels
36 | },
37 |
38 | {
39 | path: 'plugins',
40 | name: 'consolePlugins',
41 | component: components.ConsolePlugins
42 | },
43 |
44 | {
45 | path: 'services',
46 | name: 'consoleServices',
47 | component: components.ConsoleServices
48 | },
49 |
50 | {
51 | path: 'vuex',
52 | name: 'consoleVuex',
53 | component: components.ConsoleVuex
54 | }
55 |
56 | ]
57 |
58 | }
59 |
60 | ];
61 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-fill.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin fill {
3 | position: absolute;
4 | top: 0;
5 | left: 0;
6 | box-sizing: border-box;
7 | width: 100%;
8 | height: 100%;
9 | }
10 |
11 | @mixin fill-width {
12 | position: absolute;
13 | left: 0;
14 | box-sizing: border-box;
15 | width: 100%;
16 | }
17 |
18 | @mixin fill-height {
19 | position: absolute;
20 | top: 0;
21 | box-sizing: border-box;
22 | height: 100%;
23 | }
24 |
25 |
26 |
27 | @mixin fill-relative {
28 | position: relative;
29 | top: 0;
30 | left: 0;
31 | box-sizing: border-box;
32 | width: 100%;
33 | height: 100%;
34 | }
35 |
36 | @mixin fill-width-relative {
37 | position: relative;
38 | left: 0;
39 | box-sizing: border-box;
40 | width: 100%;
41 | }
42 |
43 | @mixin fill-height-relative {
44 | position: relative;
45 | top: 0;
46 | box-sizing: border-box;
47 | height: 100%;
48 | }
49 |
50 |
51 |
52 | @mixin fill-fixed {
53 | position: fixed;
54 | top: 0;
55 | left: 0;
56 | box-sizing: border-box;
57 | width: 100%;
58 | height: 100%;
59 | }
60 |
61 | @mixin fill-width-fixed {
62 | position: fixed;
63 | left: 0;
64 | box-sizing: border-box;
65 | width: 100%;
66 | }
67 |
68 | @mixin fill-height-fixed {
69 | position: fixed;
70 | top: 0;
71 | box-sizing: border-box;
72 | height: 100%;
73 | }
74 |
--------------------------------------------------------------------------------
/src/models/Post.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | import { api } from '@services';
4 |
5 | export default Vue.extend({
6 |
7 | props: {
8 |
9 | id: {
10 | type: Number,
11 | required: true
12 | },
13 |
14 | userId: {
15 | type: Number,
16 | required: true
17 | },
18 |
19 | title: {
20 | type: String
21 | },
22 |
23 | body: {
24 | type: String
25 | },
26 |
27 | comments: {
28 | type: Array,
29 | default: function () {
30 | return [];
31 | }
32 | }
33 |
34 | },
35 |
36 | data: function () {
37 | return {
38 | user: null
39 | };
40 | },
41 |
42 | computed: {
43 |
44 | hasComments: function () {
45 | return this.comments.length ? true : false;
46 | },
47 |
48 | excerpt: function () {
49 | return this.body.substr(0, 300);
50 | }
51 |
52 | },
53 |
54 | methods: {
55 |
56 | fetchComments: function () {
57 | let post = this;
58 | api.getCommentsForPost(this.id, function (response) {
59 | post.comments = response.data;
60 | });
61 | },
62 |
63 | fetchUser: function () {
64 | let post = this;
65 | api.getUser(this.userId, function (response) {
66 | post.user = response.data;
67 | });
68 | }
69 |
70 | },
71 |
72 | created: function () {
73 | // this.fetchUser();
74 | }
75 |
76 | });
77 |
--------------------------------------------------------------------------------
/src/components/console/ConsoleVuex.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
{{ globalCounterValues }}
40 |
41 |
42 | Operate counter here and now
43 | Increment
44 | Decrement
45 |
46 | Operate via a component
47 |
48 |
49 | Operate via a component in a popover
50 | Open popover
51 |
52 |
53 |
54 |
55 |
56 |
59 |
--------------------------------------------------------------------------------
/src/styles/base/base-inputs.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Labels
7 |
8 | label {
9 | @include cursor-pointer;
10 | }
11 |
12 |
13 |
14 | // Reset input styling
15 | button,
16 | input,
17 | textarea {
18 | font-family: inherit;
19 | font-size: inherit;
20 | line-height: inherit;
21 |
22 | padding: 0;
23 |
24 | border-width: 0;
25 | border-style: solid;
26 | outline-style: solid;
27 | border-radius: 0;
28 |
29 | vertical-align: middle;
30 |
31 | color: inherit;
32 | outline-color: transparent;
33 |
34 | background-color: transparent;
35 |
36 | &:focus {
37 | outline-width: 0;
38 | outline-offset: 0;
39 | }
40 |
41 | appearance: none;
42 |
43 | // &[type="search"] {
44 | // appearance: textfield;
45 | // }
46 |
47 | transition-property: color, border-color, background-color, box-shadow;
48 | }
49 |
50 | // Dimensions
51 | input,
52 | textarea {
53 | cursor: inherit;
54 | box-sizing: border-box;
55 | max-width: 100%;
56 | // border-color: transparent;
57 | // padding: ($pad-vertical - 1px) ($pad-horizontal - 1px);
58 | // border-width: 1px;
59 |
60 | &.block {
61 | width: 100%;
62 | max-height: 100%;
63 | }
64 |
65 | }
66 |
67 | // textarea {
68 | // height: 10.72em;
69 | // &.squeeze {
70 | // height: 4.02em;
71 | // }
72 | // }
73 |
74 | ::placeholder {
75 | color: $color-grey;
76 | }
77 |
--------------------------------------------------------------------------------
/src/services/network.js:
--------------------------------------------------------------------------------
1 |
2 | import Vue from 'vue';
3 |
4 | export default new Vue({
5 |
6 | data: function () {
7 | return {
8 | isOnline: false,
9 | isConnecting: true
10 | };
11 | },
12 |
13 | computed: {
14 |
15 | isOffline: function () {
16 | return !this.isOnline;
17 | }
18 |
19 | },
20 |
21 | methods: {
22 |
23 | setOffline: function () {
24 | this.isOnline = false;
25 | return this;
26 | },
27 |
28 | setOnline: function () {
29 | this.isOnline = true;
30 | return this;
31 | },
32 |
33 | getOnlineStatus: function () {
34 | return window.navigator.onLine ? true : false;
35 | },
36 |
37 | updateOnlineStatus: function () {
38 | this.isOnline = this.getOnlineStatus();
39 | return this;
40 | },
41 |
42 | setListeners: function () {
43 | window.addEventListener('online', this.updateOnlineStatus);
44 | window.addEventListener('offline', this.updateOnlineStatus);
45 | },
46 |
47 | removeListeners: function () {
48 | window.removeEventListener('online', this.updateOnlineStatus);
49 | window.removeEventListener('offline', this.updateOnlineStatus);
50 | }
51 |
52 | },
53 |
54 | created: function () {
55 | this.updateOnlineStatus();
56 | this.isConnecting = false;
57 | this.setListeners();
58 | },
59 |
60 | beforeDestroy: function () {
61 | this.removeListeners();
62 | }
63 |
64 | });
65 |
--------------------------------------------------------------------------------
/src/styles/base/base-type.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | html {
7 | font-family: $font-sans;
8 | font-size: $font-size-default;
9 | // font-weight: 300;
10 |
11 | @include viewport-over-verylarge {
12 | font-size: $font-size-default-over-verylarge;
13 | }
14 |
15 | // @include viewport-over($breakpoint-ridiculous) {
16 | // font-size: 1.5 * $font-size-default;
17 | // }
18 |
19 | // @include viewport-over($breakpoint-totallyridiculous) {
20 | // font-size: 2 * $font-size-default;
21 | // }
22 |
23 | }
24 |
25 | blockquote,
26 | q {
27 | font-family: $font-sans;
28 | }
29 |
30 | i {
31 | svg {
32 | display: block;
33 | width: 1em;
34 | height: 1em;
35 | }
36 | }
37 |
38 | address {
39 | font-style: inherit;
40 | }
41 |
42 | // Headings
43 | h2,
44 | h3,
45 | h4 {
46 | font-weight: inherit;
47 | }
48 |
49 | h3,
50 | h4,
51 | h5 {
52 | font-size: inherit;
53 | }
54 |
55 | h1 {
56 | @include type-h1;
57 | }
58 |
59 | h2 {
60 | @include type-h2;
61 | }
62 |
63 | h3 {
64 | @include type-h3;
65 | }
66 |
67 | h4 {
68 | @include type-h4;
69 | }
70 |
71 | th,
72 | dt,
73 | h5 {
74 | @include type-h5;
75 | }
76 |
77 |
78 |
79 | // Code
80 | code {
81 | white-space: nowrap;
82 | }
83 |
84 | pre {
85 | code {
86 | white-space: inherit;
87 | }
88 | }
89 |
90 | pre,
91 | code,
92 | kbd,
93 | samp {
94 | @include type-mono;
95 | }
96 |
--------------------------------------------------------------------------------
/src/styles/shared.scss:
--------------------------------------------------------------------------------
1 |
2 | // This shared.scss can easily be imported in components and global style files to ensure all mixins and constants are available
3 | // NOTE: shared SCSS should NOT output any CSS. This way it can be imported anywhere without duplicating output.
4 |
5 | // Constants, functions etc.
6 | @import './definitions/functions';
7 | @import './definitions/constants';
8 |
9 | // Atomic style properties as mixins
10 | @import './mixins/mixin-animation';
11 | @import './mixins/mixin-background';
12 | @import './mixins/mixin-box-model';
13 | @import './mixins/mixin-buffer';
14 | @import './mixins/mixin-clear';
15 | @import './mixins/mixin-container';
16 | @import './mixins/mixin-cursor';
17 | @import './mixins/mixin-display';
18 | @import './mixins/mixin-fill';
19 | @import './mixins/mixin-font';
20 | @import './mixins/mixin-hide';
21 | @import './mixins/mixin-keep';
22 | @import './mixins/mixin-limit';
23 | @import './mixins/mixin-overflow';
24 | @import './mixins/mixin-overlay';
25 | @import './mixins/mixin-pad';
26 | @import './mixins/mixin-pull';
27 | @import './mixins/mixin-push';
28 | @import './mixins/mixin-radius';
29 | @import './mixins/mixin-rhythm';
30 | @import './mixins/mixin-row';
31 | @import './mixins/mixin-shadow';
32 | @import './mixins/mixin-transform';
33 | @import './mixins/mixin-transitions';
34 | @import './mixins/mixin-type';
35 | @import './mixins/mixin-viewport';
36 |
--------------------------------------------------------------------------------
/src/components/pages/PageConsole.vue:
--------------------------------------------------------------------------------
1 |
2 |
61 |
62 |
63 |
64 |
65 |
66 | Debug {{ title }}
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-hide.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .hide {
7 | display: none;
8 | }
9 |
10 |
11 |
12 | // Media
13 |
14 | .hide-in-print {
15 | @include hide-in-print;
16 | }
17 |
18 | .hide-in-not-print {
19 | @include hide-in-not-print;
20 | }
21 |
22 |
23 |
24 | // Orientation
25 |
26 | .hide-in-landscape {
27 | @include hide-in-landscape;
28 | }
29 |
30 | .hide-in-portrait {
31 | @include hide-in-portrait;
32 | }
33 |
34 |
35 |
36 | // Responsive
37 |
38 | .hide-under-tiny {
39 | @include hide-under-tiny;
40 | }
41 |
42 | .hide-over-tiny {
43 | @include hide-over-tiny;
44 | }
45 |
46 |
47 |
48 | .hide-under-small {
49 | @include hide-under-small;
50 | }
51 |
52 | .hide-over-small {
53 | @include hide-over-small;
54 | }
55 |
56 |
57 |
58 | .hide-under-smallish {
59 | @include hide-under-smallish;
60 | }
61 |
62 | .hide-over-smallish {
63 | @include hide-over-smallish;
64 | }
65 |
66 |
67 |
68 | .hide-under-medium {
69 | @include hide-under-medium;
70 | }
71 |
72 | .hide-over-medium {
73 | @include hide-over-medium;
74 | }
75 |
76 |
77 |
78 | .hide-under-large {
79 | @include hide-under-large;
80 | }
81 |
82 | .hide-over-large {
83 | @include hide-over-large;
84 | }
85 |
86 |
87 |
88 | .hide-under-verylarge {
89 | @include hide-under-verylarge;
90 | }
91 |
92 | .hide-over-verylarge {
93 | @include hide-over-verylarge;
94 | }
95 |
--------------------------------------------------------------------------------
/src/styles/utilities-composed/utility-bodytext.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | // Text context
7 |
8 | // This is used for rich body text. Elements commonly used in building components, like links, are robbed of their sensible defaults. Attach this utility class on a body text container to style the rich text elements in a way that makes sense for natural article content.
9 |
10 | .bodytext {
11 |
12 | // Links
13 | a {
14 | font-weight: 500;
15 | color: $color-link;
16 |
17 | &:hover,
18 | &:active {
19 | color: $color-link-active;
20 | }
21 |
22 | }
23 |
24 | strong {
25 | font-weight: 600;
26 | }
27 |
28 | // Code
29 | dt,
30 | h3,
31 | h4 {
32 | code {
33 | text-transform: none;
34 | }
35 | }
36 |
37 | code {
38 | padding: 0 0.2em;
39 | @include radius-tight;
40 | background-color: $color-verylightgrey;
41 | box-shadow: 0 0 0 1px $color-lightgrey;
42 | }
43 |
44 | // FIXME: duplicated from base/o-code.scss
45 | pre {
46 | code {
47 | @include radius-loose;
48 | padding: 1.6em;
49 | box-shadow: none;
50 | }
51 | }
52 |
53 |
54 | // Lists
55 | ul,
56 | ol {
57 | margin-left: 2em;
58 |
59 | li {
60 | margin-bottom: 0.4em;
61 | }
62 |
63 | ul,
64 | ol {
65 | margin-bottom: 0.8em;
66 | }
67 |
68 | }
69 |
70 | ul {
71 | list-style-type: disc;
72 | }
73 |
74 | ol {
75 | list-style-type: disc;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/tooling/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | require('babel-register')
3 |
4 | var config = require('../../tooling/env')
5 | var aliases = require('../../src/config/config.aliases')
6 | var specPath = aliases['@spec'] + '/e2e'
7 |
8 | // http://nightwatchjs.org/gettingstarted#settings-file
9 | module.exports = {
10 | globals_path: path.join(__dirname, './nightwatch.globals.js'),
11 |
12 | src_folders: [specPath],
13 | output_folder: 'reports/e2e',
14 | custom_assertions_path: ['tooling/e2e/custom-assertions'],
15 |
16 | selenium: {
17 | start_process: true,
18 | server_path: require('selenium-server').path,
19 | host: '127.0.0.1',
20 | port: 4444,
21 | cli_args: {
22 | 'webdriver.chrome.driver': require('chromedriver').path
23 | }
24 | },
25 |
26 | test_settings: {
27 | default: {
28 | selenium_port: 4444,
29 | selenium_host: 'localhost',
30 | silent: true,
31 | globals: {
32 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
33 | }
34 | },
35 |
36 | chrome: {
37 | desiredCapabilities: {
38 | browserName: 'chrome',
39 | javascriptEnabled: true,
40 | acceptSslCerts: true
41 | }
42 | },
43 |
44 | firefox: {
45 | desiredCapabilities: {
46 | browserName: 'firefox',
47 | javascriptEnabled: true,
48 | acceptSslCerts: true
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tooling/check-versions.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk')
2 | var semver = require('semver')
3 | var packageConfig = require('../package.json')
4 | var shell = require('shelljs')
5 | function exec (cmd) {
6 | return require('child_process').execSync(cmd).toString().trim()
7 | }
8 |
9 | var versionRequirements = [
10 | {
11 | name: 'node',
12 | currentVersion: semver.clean(process.version),
13 | versionRequirement: packageConfig.engines.node
14 | },
15 | ]
16 |
17 | if (shell.which('npm')) {
18 | versionRequirements.push({
19 | name: 'npm',
20 | currentVersion: exec('npm --version'),
21 | versionRequirement: packageConfig.engines.npm
22 | })
23 | }
24 |
25 | module.exports = function () {
26 | var warnings = []
27 | for (var i = 0; i < versionRequirements.length; i++) {
28 | var mod = versionRequirements[i]
29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
30 | warnings.push(mod.name + ': ' +
31 | chalk.red(mod.currentVersion) + ' should be ' +
32 | chalk.green(mod.versionRequirement)
33 | )
34 | }
35 | }
36 |
37 | if (warnings.length) {
38 | console.log('')
39 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
40 | console.log()
41 | for (var i = 0; i < warnings.length; i++) {
42 | var warning = warnings[i]
43 | console.log(' ' + warning)
44 | }
45 | console.log()
46 | process.exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/services/time.js:
--------------------------------------------------------------------------------
1 |
2 | import Vue from 'vue';
3 |
4 | export default new Vue({
5 |
6 | data: function () {
7 | return {
8 | current: new Date(),
9 | _timer: null
10 | };
11 | },
12 |
13 | computed: {
14 |
15 | },
16 |
17 | methods: {
18 |
19 | // Update the current time with a new Date object
20 | setCurrentTime: function () {
21 | this.current = new Date();
22 | return this;
23 | },
24 |
25 | // Throttled callback for each update.
26 | onTimerUpdate: function () {
27 | window.requestAnimationFrame(this.setCurrentTime);
28 | return this;
29 | },
30 |
31 | // Start timer and regular updates. Timer will run until `stopTimer` is called. If timer is already running, it will be stopped and then restarted.
32 | startTimer: function () {
33 | var clock = this;
34 |
35 | // Setup
36 | clock.stopTimer();
37 |
38 | // Update immediately for the first time
39 | clock.onTimerUpdate();
40 |
41 | // Start interval
42 | clock._timer = setInterval(function () {
43 | clock.onTimerUpdate();
44 | }, 1000);
45 |
46 | return clock;
47 | },
48 |
49 | // Stop updating the current time. `time` will keep the last time.
50 | stopTimer: function () {
51 |
52 | if (this._timer) {
53 | clearInterval(this._timer);
54 | this._timer = null;
55 | }
56 |
57 | return this;
58 | }
59 |
60 | },
61 |
62 | created: function () {
63 | this.onTimerUpdate();
64 | this.startTimer();
65 | }
66 |
67 | });
68 |
--------------------------------------------------------------------------------
/src/components/layout/TitlebarMenu.vue:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
51 |
52 |
53 |
54 |
65 |
--------------------------------------------------------------------------------
/src/components/counters/LocalCounter.vue:
--------------------------------------------------------------------------------
1 |
2 |
57 |
58 |
59 |
60 |
61 | {{ label }}:
62 | {{ valueToRender }}
63 |
64 |
65 |
66 |
67 |
80 |
--------------------------------------------------------------------------------
/src/styles/definitions/functions.scss:
--------------------------------------------------------------------------------
1 |
2 | // Custom functions for SCSS
3 | // NOTE
4 | // - See built-in functions: http://sass-lang.com/documentation/Sass/Script/Functions.html
5 | // - If you find yourself using built-in functions with the same values often, considering creating a wrapper function in this file
6 |
7 |
8 |
9 | // Remove the unit of a length
10 | @function number ($number) {
11 | @if type-of($number) == 'number' and not unitless($number) {
12 | @return $number / ($number * 0 + 1);
13 | }
14 | @return $number;
15 | }
16 |
17 |
18 |
19 | // Color handling
20 |
21 | // Make translucent
22 | @function color-transparent ($color) {
23 | @return color-translucent($color, 0);
24 | }
25 |
26 | // Make color translucent
27 | // FIXME: should return 0.1 for black, 0.25 for light values
28 | @function color-translucent ($color, $strength: 0.15) {
29 | @return rgba($color, $strength);
30 | }
31 |
32 | // FIXME: should adjust based on hue
33 | @function color-translucent-light ($color) {
34 | @return color-translucent($color, 0.4);
35 | }
36 |
37 | // FIXME: should adjust based on hue
38 | @function color-translucent-verylight ($color) {
39 | @return color-translucent($color, 0.9);
40 | }
41 |
42 |
43 |
44 | // Make color darker
45 | @function color-darker ($color, $strength: 10%) {
46 | @return darken($color, $strength);
47 | }
48 |
49 | // Make color more saturated
50 | @function color-saturate ($color, $strength: 10%) {
51 | @return saturate(darken($color, 1.5 * $strength), $strength);
52 | }
53 |
--------------------------------------------------------------------------------
/src/config/config.aliases.js:
--------------------------------------------------------------------------------
1 |
2 | // Aliases usable in codebase when doing imports and resolving URLs. From project root.
3 | // NOTE: unlike other configuration files, this is needed by the low-level tooling scripts BEFORE any aliases have been defined and full URL resolution is working (since those aliases are defined here, duh). That's why this is split from other configuration files.
4 | module.exports = {
5 |
6 | // src root
7 | // NOTE: prefer the other aliases over
8 | '@': 'src',
9 |
10 | // The base configuration (alias is mostly for the client)
11 | // NOTE: for client-side code it would be better to use a JS utility that reads configuration (merging values from multiple sources) instead of using these raw values in application code
12 | '@config': 'src/config',
13 | '@locales': 'src/locales',
14 |
15 | // Vendor code, services, utilities etc.
16 | '@models': 'src/models',
17 | '@services': 'src/services',
18 | '@util': 'src/util',
19 |
20 | // Assets
21 | '@assets': 'src/assets',
22 | '@fonts': 'src/fonts',
23 | '@svg': 'src/svg',
24 |
25 | // Vue application code
26 | '@vue-components': 'src/components',
27 | '@vue-directives': 'src/vue/directives',
28 | '@vue-mixins': 'src/vue/mixins',
29 | '@vue-plugins': 'src/vue/plugins',
30 |
31 | // State management
32 | '@store': 'src/store',
33 |
34 | // Global styles
35 | // NOTE: we could split this further
36 | '@styles': 'src/styles',
37 | '@shared-styles': 'src/styles/shared',
38 |
39 | // Test cases
40 | '@spec': 'spec'
41 |
42 | };
43 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-buffer.scss:
--------------------------------------------------------------------------------
1 |
2 | // To avoid code duplication, the utility classes are produced by a mixin
3 | @import '~@styles/toolchain/toolchain-utility-buffer';
4 |
5 |
6 |
7 | // Default
8 | @include toolchain-utility-buffer;
9 |
10 | // Responsive versions
11 | @include viewport-under-tiny {
12 | @include toolchain-utility-buffer('under-tiny');
13 | }
14 |
15 | @include viewport-over-tiny {
16 | @include toolchain-utility-buffer('over-tiny');
17 | }
18 |
19 | @include viewport-under-small {
20 | @include toolchain-utility-buffer('under-small');
21 | }
22 |
23 | @include viewport-over-small {
24 | @include toolchain-utility-buffer('over-small');
25 | }
26 |
27 | @include viewport-under-smallish {
28 | @include toolchain-utility-buffer('under-smallish');
29 | }
30 |
31 | @include viewport-over-smallish {
32 | @include toolchain-utility-buffer('over-smallish');
33 | }
34 |
35 | @include viewport-under-medium {
36 | @include toolchain-utility-buffer('under-medium');
37 | }
38 |
39 | @include viewport-over-medium {
40 | @include toolchain-utility-buffer('over-medium');
41 | }
42 |
43 | @include viewport-under-large {
44 | @include toolchain-utility-buffer('under-large');
45 | }
46 |
47 | @include viewport-over-large {
48 | @include toolchain-utility-buffer('over-large');
49 | }
50 |
51 | @include viewport-under-verylarge {
52 | @include toolchain-utility-buffer('under-verylarge');
53 | }
54 |
55 | @include viewport-over-verylarge {
56 | @include toolchain-utility-buffer('over-verylarge');
57 | }
58 |
--------------------------------------------------------------------------------
/src/styles/global.scss:
--------------------------------------------------------------------------------
1 |
2 | // CSS declarations
3 |
4 | // Keyframe animations
5 | @import './keyframes/keyframes-pulse';
6 | @import './keyframes/keyframes-spin';
7 |
8 | // Web fonts
9 | @import './webfonts/webfont-source-sans';
10 |
11 |
12 |
13 | // Vendor code
14 | //
15 | // NOTE
16 | //
17 | // - only include vendor code that you want to build with the custom build pipeline (probably rarely if ever)
18 | // - precompiled vendor code should be included in index.html.ejs
19 |
20 | // @import './vendor/bootstrap-source';
21 |
22 |
23 |
24 | // Vendor overrides
25 | //
26 | // NOTE
27 | // - many times the CSS code delivered with plugins causes a lot of issues and needs additional adjustments when it is included globally
28 | // - this is the place for it, so all these hacky overrides are in one place
29 | // - sometimes it is even better to not include the distributed vendor CSS at all and just rewrite the CSS without having to override anything
30 |
31 | @import './vendor-overrides/iphone-inline-video';
32 |
33 |
34 |
35 | // Base CSS
36 |
37 | // Normalizing defaults
38 | @import './normalize/normalize';
39 | @import './normalize/defaults';
40 |
41 | // Global base styling
42 | @import './base/base-code';
43 | @import './base/base-colors';
44 | @import './base/base-hr';
45 | @import './base/base-images';
46 | @import './base/base-inputs';
47 | @import './base/base-links';
48 | @import './base/base-lists';
49 | @import './base/base-rhythm';
50 | @import './base/base-tables';
51 | @import './base/base-tooltips';
52 | @import './base/base-type';
53 |
--------------------------------------------------------------------------------
/spec/models/Post.spec.js:
--------------------------------------------------------------------------------
1 | // import Vue from 'vue';
2 | import { Comment, Post } from '@models';
3 |
4 | describe('Post model', function () {
5 |
6 | it('should have empty comments list when none is passed', function () {
7 |
8 | // Set up a new instance of model
9 | var post = new Post({
10 | propsData: {
11 | id: 1,
12 | userId: 1
13 | }
14 | });
15 |
16 | // Expected results
17 | expect(post.comments).to.be.a('array');
18 | expect(post.comments.length).to.equal(0);
19 |
20 | });
21 |
22 | it('should have no title when none is passed', function () {
23 | var post = new Post({
24 | propsData: {
25 | id: 1,
26 | userId: 1
27 | }
28 | });
29 | expect(post.title).to.not.be.ok;
30 | });
31 |
32 | it('should have no body when none is passed', function () {
33 | var post = new Post({
34 | propsData: {
35 | id: 1,
36 | userId: 1
37 | }
38 | });
39 | expect(post.body).to.not.be.ok;
40 | });
41 |
42 | it('should have hasComments as false when none is passed', function () {
43 | var post = new Post({
44 | propsData: {
45 | id: 1,
46 | userId: 1
47 | }
48 | });
49 | expect(post.hasComments).to.not.be.ok;
50 | });
51 |
52 | it('should have hasComments as true when is passed', function () {
53 | var post = new Post({
54 | propsData: {
55 | id: 1,
56 | userId: 1,
57 | comments: [
58 |
59 | new Comment({
60 | propsData: {
61 | id: 1,
62 | postId: 1
63 | }
64 | })
65 |
66 | ]
67 | }
68 | });
69 | expect(post.hasComments).to.be.ok;
70 | });
71 |
72 | });
73 |
--------------------------------------------------------------------------------
/src/config/config.routes.js:
--------------------------------------------------------------------------------
1 |
2 | // Configuration for vue-router
3 | // NOTE: This is in a separate file, because Webpack has to be already running with aliases for the imports to work
4 | // NOTE: could maybe use `require.context` to make this more dynamic
5 | import components from '@vue-components';
6 |
7 | export default [
8 |
9 | // {
10 | // path: '*',
11 | // name: 'notFound',
12 | // component: components.PageNotFound
13 | // },
14 |
15 | {
16 | // NOTE
17 | // - We could just display the home page component with this route
18 | // - But if we did, the router will think all top-level pages are children of that
19 | // - We also generally don't want more than one route for the same page
20 | // - So to allow users to use index paths, it's better to redirect to the default child page
21 | path: '/',
22 | name: 'root',
23 | redirect: {
24 | name: 'home'
25 | }
26 | },
27 |
28 | {
29 | path: '/home',
30 | name: 'home',
31 | component: components.PageHome
32 | },
33 |
34 | {
35 | path: '/login',
36 | name: 'login',
37 | component: components.PageLogin
38 | },
39 |
40 | // NOTE
41 | // `page` here is optional, indicated by the question mark
42 | // See https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js#L6
43 | {
44 | path: '/posts/:page?',
45 | name: 'posts',
46 | component: components.PagePosts
47 | },
48 |
49 | {
50 | path: '/post/:id',
51 | name: 'post',
52 | component: components.PagePost
53 | },
54 |
55 | {
56 | path: '/secret',
57 | name: 'secret',
58 | component: components.PageHome
59 | }
60 |
61 | ];
62 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-pull.scss:
--------------------------------------------------------------------------------
1 |
2 | // To avoid code duplication, the utility classes are produced by a mixin
3 | @import '~@shared-styles';
4 | @import '~@styles/toolchain/toolchain-utility-pull';
5 |
6 |
7 |
8 | // Default
9 | @include toolchain-utility-pull;
10 |
11 | // Responsive versions
12 | @include viewport-under-tiny {
13 | @include toolchain-utility-pull('under-tiny');
14 | }
15 |
16 | @include viewport-over-tiny {
17 | @include toolchain-utility-pull('over-tiny');
18 | }
19 |
20 |
21 |
22 | @include viewport-under-small {
23 | @include toolchain-utility-pull('under-small');
24 | }
25 |
26 | @include viewport-over-small {
27 | @include toolchain-utility-pull('over-small');
28 | }
29 |
30 |
31 |
32 | @include viewport-under-smallish {
33 | @include toolchain-utility-pull('under-smallish');
34 | }
35 |
36 | @include viewport-over-smallish {
37 | @include toolchain-utility-pull('over-smallish');
38 | }
39 |
40 |
41 |
42 | @include viewport-under-medium {
43 | @include toolchain-utility-pull('under-medium');
44 | }
45 |
46 | @include viewport-over-medium {
47 | @include toolchain-utility-pull('over-medium');
48 | }
49 |
50 |
51 |
52 | @include viewport-under-large {
53 | @include toolchain-utility-pull('under-large');
54 | }
55 |
56 | @include viewport-over-large {
57 | @include toolchain-utility-pull('over-large');
58 | }
59 |
60 |
61 |
62 | @include viewport-under-verylarge {
63 | @include toolchain-utility-pull('under-verylarge');
64 | }
65 |
66 | @include viewport-over-verylarge {
67 | @include toolchain-utility-pull('over-verylarge');
68 | }
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-push.scss:
--------------------------------------------------------------------------------
1 |
2 | // To avoid code duplication, the utility classes are produced by a mixin
3 | @import '~@shared-styles';
4 | @import '~@styles/toolchain/toolchain-utility-push';
5 |
6 |
7 |
8 | // Default
9 | @include toolchain-utility-push;
10 |
11 | // Responsive versions
12 | @include viewport-under-tiny {
13 | @include toolchain-utility-push('under-tiny');
14 | }
15 |
16 | @include viewport-over-tiny {
17 | @include toolchain-utility-push('over-tiny');
18 | }
19 |
20 |
21 |
22 | @include viewport-under-small {
23 | @include toolchain-utility-push('under-small');
24 | }
25 |
26 | @include viewport-over-small {
27 | @include toolchain-utility-push('over-small');
28 | }
29 |
30 |
31 |
32 | @include viewport-under-smallish {
33 | @include toolchain-utility-push('under-smallish');
34 | }
35 |
36 | @include viewport-over-smallish {
37 | @include toolchain-utility-push('over-smallish');
38 | }
39 |
40 |
41 |
42 | @include viewport-under-medium {
43 | @include toolchain-utility-push('under-medium');
44 | }
45 |
46 | @include viewport-over-medium {
47 | @include toolchain-utility-push('over-medium');
48 | }
49 |
50 |
51 |
52 | @include viewport-under-large {
53 | @include toolchain-utility-push('under-large');
54 | }
55 |
56 | @include viewport-over-large {
57 | @include toolchain-utility-push('over-large');
58 | }
59 |
60 |
61 |
62 | @include viewport-under-verylarge {
63 | @include toolchain-utility-push('under-verylarge');
64 | }
65 |
66 | @include viewport-over-verylarge {
67 | @include toolchain-utility-push('over-verylarge');
68 | }
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-limit.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './mixin-viewport';
3 |
4 |
5 |
6 | // Limit width of an element when viewport is big enough
7 |
8 | @mixin limit ($breakpoint, $limit: $breakpoint) {
9 | @include viewport-over($breakpoint) {
10 | max-width: $limit;
11 | }
12 | }
13 |
14 | @mixin limit-max {
15 | max-width: 100%;
16 | }
17 |
18 | @mixin limit-tiny {
19 | @include viewport-over-tiny {
20 | max-width: $limit-tiny;
21 | }
22 | }
23 |
24 | @mixin limit-small {
25 | @include viewport-over-small {
26 | max-width: $limit-small;
27 | }
28 | }
29 |
30 | @mixin limit-smallish {
31 | @include viewport-over-smallish {
32 | max-width: $limit-smallish;
33 | }
34 | }
35 |
36 | @mixin limit-medium {
37 | @include viewport-over-medium {
38 | max-width: $limit-medium;
39 | }
40 | }
41 |
42 | @mixin limit-large {
43 | @include viewport-over-large {
44 | max-width: $limit-large;
45 | }
46 | }
47 |
48 | @mixin limit-verylarge {
49 | @include viewport-over-verylarge {
50 | max-width: $limit-verylarge;
51 | }
52 | }
53 |
54 | @mixin limit-ridiculous {
55 | @include viewport-over-ridiculous {
56 | max-width: $limit-ridiculous;
57 | }
58 | }
59 |
60 | @mixin limit-totallyridiculous {
61 | @include viewport-over-totallyridiculous {
62 | max-width: $limit-totallyridiculous;
63 | }
64 | }
65 |
66 | @mixin no-limit {
67 | max-width: none;
68 | }
69 |
70 |
71 |
72 | // Limit height of an element when viewport is big enough
73 |
74 | @mixin limit-height ($breakpoint, $limit: $breakpoint) {
75 | @include viewport-height-over($breakpoint) {
76 | max-height: $limit;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tooling/env/index.js:
--------------------------------------------------------------------------------
1 |
2 | // see http://vuejs-templates.github.io/webpack for documentation.
3 | var path = require('path')
4 |
5 | module.exports = {
6 | build: {
7 | env: require('./prod.env'),
8 | index: path.resolve(__dirname, '../../dist/index.html'),
9 | assetsRoot: path.resolve(__dirname, '../../dist'),
10 | assetsSubDirectory: 'static',
11 | assetsPublicPath: '/',
12 | productionSourceMap: true,
13 | // Gzip off by default as many popular static hosts such as
14 | // Surge or Netlify already gzip all static assets for you.
15 | // Before setting to `true`, make sure to:
16 | // npm install --save-dev compression-webpack-plugin
17 | productionGzip: false,
18 | productionGzipExtensions: ['js', 'css'],
19 | // Run the build command with an extra argument to
20 | // View the bundle analyzer report after build finishes:
21 | // `npm run build --report`
22 | // Set to `true` or `false` to always turn it on or off
23 | bundleAnalyzerReport: process.env.npm_config_report
24 | },
25 | dev: {
26 | env: require('./dev.env'),
27 | port: 8888,
28 | autoOpenBrowser: true,
29 | assetsSubDirectory: 'static',
30 | assetsPublicPath: '/',
31 | proxyTable: {},
32 | // CSS Sourcemaps off by default because relative paths are "buggy"
33 | // with this option, according to the CSS-Loader README
34 | // (https://github.com/webpack/css-loader#sourcemaps)
35 | // In our experience, they generally work as expected,
36 | // just be aware of this issue when enabling this option.
37 | cssSourceMap: false
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/snippets/PicSvg.vue:
--------------------------------------------------------------------------------
1 |
2 |
60 |
61 |
62 |
63 |
64 | {{ renderedTitle }}
65 |
66 |
67 |
68 |
69 |
70 |
74 |
--------------------------------------------------------------------------------
/src/util/composeClassnames.js:
--------------------------------------------------------------------------------
1 | import { isNumber, isString, kebabCase } from 'lodash';
2 |
3 | // Generate HTML/CSS class names based on a set of state, with prefixes and negatives added
4 | export default function (stateHash, prefixPositive, prefixNegative) {
5 | var classes = [];
6 |
7 | // Custom prefixes
8 | if (prefixPositive) {
9 |
10 | // Only positive prefix was passed, using it as the base for negative as well
11 | if (!prefixNegative) {
12 | prefixNegative = prefixPositive + '-not';
13 | }
14 |
15 | // Default prefixes
16 | } else {
17 | prefixPositive = 'is';
18 | prefixPositive = 'not';
19 | }
20 |
21 | // State classes
22 | for (var key in stateHash) {
23 | var className;
24 |
25 | // String/number value goes into the class name
26 | if (isString(stateHash[key]) || isNumber(stateHash[key])) {
27 | className = key + '-' + stateHash[key];
28 |
29 | // Otherwise we use boolean classnames
30 | } else {
31 |
32 | // Prevent duplicating prefixes if they're passed in the keys
33 | if (key.substr(0, prefixPositive.length) === prefixPositive) {
34 | className = key.substr(prefixPositive.length);
35 |
36 | } else if (key.substr(0, prefixPositive.length) === prefixPositive) {
37 | className = key.substr(prefixPositive.length);
38 |
39 | // Nothing to sanitize
40 | } else {
41 | className = key;
42 | }
43 |
44 | }
45 |
46 | // Compose prefix + value
47 | // Turn into kebab-case
48 | // Push into results array
49 | classes.push(kebabCase((stateHash[key] ? prefixPositive : prefixNegative) + '-' + className));
50 |
51 | }
52 |
53 | return classes;
54 | }
55 |
--------------------------------------------------------------------------------
/src/.htmllintrc:
--------------------------------------------------------------------------------
1 | {
2 | "attr-bans": [
3 | "align",
4 | "background",
5 | "bgcolor",
6 | "border",
7 | "dynsrc",
8 | "id",
9 | "frameborder",
10 | "longdesc",
11 | "lowsrc",
12 | "onclick",
13 | "ondblclick",
14 | "onload",
15 | "marginwidth",
16 | "marginheight",
17 | "scrolling",
18 | "style",
19 | "width"
20 | ],
21 | "attr-name-ignore-regex": false,
22 | "attr-name-style": false,
23 | "attr-no-dup": true,
24 | "attr-no-unsafe-char": false,
25 | "attr-quote-style": "double",
26 | "attr-req-value": false,
27 | "class-no-dup": true,
28 | "class-style": "dash",
29 | "csslint": false,
30 | "doctype-first": false,
31 | "doctype-html5": false,
32 | "fig-req-figcaption": true,
33 | "focusable-tabindex-style": false,
34 | "head-req-title": true,
35 | "href-style": false,
36 | "html-req-lang": true,
37 | "id-class-ignore-regex": false,
38 | "id-class-no-ad": true,
39 | "id-class-style": "dash",
40 | "id-no-dup": true,
41 | "input-radio-req-name": true,
42 | "img-req-alt": false,
43 | "img-req-src": false,
44 | "indent-style": "tabs",
45 | "indent-width-cont": true,
46 | "input-req-label": false,
47 | "label-req-for": false,
48 | "lang-style": "case",
49 | "line-end-style": "lf",
50 | "line-max-len": false,
51 | "line-max-len-ignore-regex": "/href/g",
52 | "page-title": true,
53 | "spec-char-escape": false,
54 | "table-req-caption": false,
55 | "table-req-header": false,
56 | "tag-bans": [
57 | "b",
58 | "i",
59 | "keygen"
60 | ],
61 | "tag-close": true,
62 | "tag-name-lowercase": true,
63 | "tag-name-match": true,
64 | "tag-self-close": "never",
65 | "title-max-len": 60,
66 | "title-no-dup": true
67 | }
68 |
--------------------------------------------------------------------------------
/src/svg/power.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | svg/power
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/services/env.js:
--------------------------------------------------------------------------------
1 | // Env service
2 | //
3 | // - OS detection
4 | // - Feature detection
5 | // - Dev/prod handling
6 | // - Access to current remote
7 | import Vue from 'vue';
8 |
9 | import { Remote } from '@models';
10 | import config from '@config';
11 |
12 | const macosPlatforms = ['macintosh', 'macintel', 'macppc', 'mac68k'];
13 | const windowsPlatforms = ['win32', 'win64', 'windows', 'wince'];
14 | const iosPlatforms = ['iphone', 'ipad', 'ipod'];
15 |
16 | export default new Vue({
17 |
18 | data: function () {
19 | return {
20 |
21 | os: this.getOsFromNavigator(window.navigator.userAgent, window.navigator.platform),
22 |
23 | currentRemote: new Remote({
24 | propsData: config.remotes[config.currentRemote]
25 | })
26 |
27 | };
28 | },
29 |
30 | computed: {
31 |
32 | isDevelopment: function () {
33 | return config.env === 'development';
34 | },
35 |
36 | isProduction: function () {
37 | return !this.isDevelopment;
38 | }
39 |
40 | },
41 |
42 | methods: {
43 |
44 | // OS checking
45 | // NOTE: platform detection is never 100 % reliable
46 | getOsFromNavigator: function (userAgent, platform) {
47 | userAgent = userAgent.toLowerCase();
48 | platform = platform.toLowerCase();
49 |
50 | if (macosPlatforms.indexOf(platform) !== -1) {
51 | return 'mac';
52 |
53 | } else if (iosPlatforms.indexOf(platform) !== -1) {
54 | return 'ios';
55 |
56 | } else if (windowsPlatforms.indexOf(platform) !== -1) {
57 | return 'windows';
58 |
59 | } else if (new RegExp('Android').test(userAgent)) {
60 | return 'android';
61 |
62 | } else if (new RegExp('Linux').test(platform)) {
63 | return 'linux';
64 | }
65 |
66 | return null;
67 | }
68 |
69 | }
70 |
71 | });
72 |
--------------------------------------------------------------------------------
/src/styles/webfonts/webfont-source-sans.scss:
--------------------------------------------------------------------------------
1 | @import '~@shared-styles';
2 |
3 | @include font-face (
4 | 'Source Sans',
5 | 200,
6 | normal,
7 | '~@fonts/source-sans/sourcesanspro-extralight-webfont'
8 | );
9 |
10 | @include font-face (
11 | 'Source Sans',
12 | 200,
13 | italic,
14 | '~@fonts/source-sans/sourcesanspro-extralightitalic-webfont'
15 | );
16 |
17 | @include font-face (
18 | 'Source Sans',
19 | 300,
20 | normal,
21 | '~@fonts/source-sans/sourcesanspro-light-webfont'
22 | );
23 |
24 | @include font-face (
25 | 'Source Sans',
26 | 300,
27 | italic,
28 | '~@fonts/source-sans/sourcesanspro-lightitalic-webfont'
29 | );
30 |
31 | @include font-face (
32 | 'Source Sans',
33 | 400,
34 | normal,
35 | '~@fonts/source-sans/sourcesanspro-regular-webfont'
36 | );
37 |
38 | @include font-face (
39 | 'Source Sans',
40 | 400,
41 | italic,
42 | '~@fonts/source-sans/sourcesanspro-italic-webfont'
43 | );
44 |
45 | @include font-face (
46 | 'Source Sans',
47 | 500,
48 | normal,
49 | '~@fonts/source-sans/sourcesanspro-semibold-webfont'
50 | );
51 |
52 | @include font-face (
53 | 'Source Sans',
54 | 500,
55 | italic,
56 | '~@fonts/source-sans/sourcesanspro-semibolditalic-webfont'
57 | );
58 |
59 | @include font-face (
60 | 'Source Sans',
61 | 600,
62 | normal,
63 | '~@fonts/source-sans/sourcesanspro-bold-webfont'
64 | );
65 |
66 | @include font-face (
67 | 'Source Sans',
68 | 600,
69 | italic,
70 | '~@fonts/source-sans/sourcesanspro-bolditalic-webfont'
71 | );
72 |
73 | @include font-face (
74 | 'Source Sans',
75 | 700,
76 | normal,
77 | '~@fonts/source-sans/sourcesanspro-black-webfont'
78 | );
79 |
80 | @include font-face (
81 | 'Source Sans',
82 | 700,
83 | italic,
84 | '~@fonts/source-sans/sourcesanspro-blackitalic-webfont'
85 | );
86 |
--------------------------------------------------------------------------------
/tooling/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 | var server = require('../../tooling/dev-server.js')
4 |
5 | // Select which driver to use based on environment variable passed
6 | var useSelenium = false
7 | if (process.env.DRIVER) {
8 | var param = ('' + process.env.DRIVER).toLowerCase()
9 | if (param === 'selenium') {
10 | useSelenium = true
11 | } else if (param !== 'chrome') {
12 | throw Error('Unknown driver requested. Supported drivers are "Selenium" and "Chrome".')
13 | }
14 | }
15 |
16 | // Drivers have different config files
17 | var testConfigFilePath = 'nightwatch.chrome.conf.js'
18 | if (useSelenium) {
19 | testConfigFilePath = 'nightwatch.conf.js'
20 | }
21 |
22 | server.ready.then(() => {
23 | // 2. run the nightwatch test suite against it
24 | // to run in additional browsers:
25 | // 1. add an entry in tooling/e2e/nightwatch.conf.js under "test_settings"
26 | // 2. add it to the --env flag below
27 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
28 | // For more information on Nightwatch's config file, see
29 | // http://nightwatchjs.org/guide#settings-file
30 | var opts = process.argv.slice(2)
31 | if (opts.indexOf('--config') === -1) {
32 | opts = opts.concat(['--config', 'tooling/e2e/' + testConfigFilePath])
33 | }
34 | if (opts.indexOf('--env') === -1) {
35 | opts = opts.concat(['--env', 'chrome'])
36 | }
37 |
38 | var spawn = require('cross-spawn')
39 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
40 |
41 | runner.on('exit', function (code) {
42 | server.close()
43 | process.exit(code)
44 | })
45 |
46 | runner.on('error', function (err) {
47 | server.close()
48 | throw err
49 | })
50 | })
51 |
--------------------------------------------------------------------------------
/src/components/pages/PageHome.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 | {{ welcomeMessage }}
19 |
20 |
21 |
22 |
Thank you for using Bellevue ! To get started, please refer to the documentation at eiskis.gitbooks.io/bellevue . Happy hacking! :)
23 |
24 |
About Vue
25 |
26 |
Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is very easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries .
27 |
28 |
If you are an experienced frontend developer and want to know how Vue compares to other libraries/frameworks, check out the Comparison with Other Frameworks .
29 |
30 |
The easiest way to try out Vue.js is using the JSFiddle Hello World example . Feel free to open it in another tab and follow along as we go through some basic examples. Or, you can simply create an .html file and include Vue as a <script>.
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 |
2 | // FIXME
3 | //
4 | // Obviously this should be split into smaller modules and multiple files
5 |
6 |
7 |
8 | // State (similar to `data` in Vue)
9 | export const state = {
10 | counter: 1
11 | };
12 |
13 | // Dynamic getters (similar to `computed` in Vue)
14 | export const getters = {
15 |
16 | canDecrementCounter: function (state, getters) {
17 | return (state.counter > 1) ? true : false;
18 | },
19 |
20 | counterSquared: function (state, getters) {
21 | return Math.pow(state.counter, 2);
22 | }
23 |
24 | };
25 |
26 | // Mutations (similar to atomic methods under `methods` in Vue)
27 | // Use these in actions with `context.commit('increment')`
28 | export const mutations = {
29 |
30 | 'INCREMENT_COUNTER': function (state) {
31 | state.counter++;
32 | },
33 |
34 | 'DECREMENT_COUNTER': function (state) {
35 | state.counter--;
36 | }
37 |
38 | };
39 |
40 | // Actions (similar to helpers under `methods` in Vue)
41 | // Use these in components with `this.$store.dispatch('increment')`
42 | // Context includes actions and getters
43 | export const actions = {
44 |
45 | increment: function (context) {
46 | context.commit('INCREMENT_COUNTER');
47 | },
48 |
49 | decrement: function (context) {
50 | if (context.getters.canDecrementCounter) {
51 | context.commit('DECREMENT_COUNTER');
52 | }
53 | },
54 |
55 | // Increment counter
56 | incrementAsync: function (context) {
57 | var callback = function () {
58 |
59 | // NOTE: this is the action, not mutation
60 | context.dispatch('increment');
61 |
62 | };
63 | setTimeout(callback, 200);
64 | }
65 |
66 | };
67 |
68 | // State management split into smaller chunks
69 | export const modules = {};
70 |
71 | // Export all parts as one object
72 | export const store = {
73 | state: state,
74 | getters: getters,
75 | mutations: mutations,
76 | actions: actions,
77 | modules: modules
78 | };
79 |
--------------------------------------------------------------------------------
/src/components/sections/BlankState.vue:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {{ title }}
43 |
44 |
45 |
46 |
47 | {{ description }}
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
89 |
--------------------------------------------------------------------------------
/src/svg/app-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | app-icon
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/components/counters/GlobalCounterIterator.vue:
--------------------------------------------------------------------------------
1 |
2 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Click me to {{ label }} the counter in Vuex.
66 |
67 |
68 |
69 |
70 | Oops! The counter in Vuex's state can't be decremented further. You should find a way to increment it!
71 |
72 |
73 |
74 |
75 |
76 |
77 |
85 |
--------------------------------------------------------------------------------
/src/services/auth.js:
--------------------------------------------------------------------------------
1 | // Auth service
2 | //
3 | // - Current login status
4 | import Vue from 'vue';
5 |
6 | import api from './api';
7 | import config from '@config';
8 |
9 | export default new Vue({
10 |
11 | data: function () {
12 | return {
13 | maxAccessLevel: config.routePermissionRoles.length,
14 | currentAccount: null,
15 | isLoading: false
16 | };
17 | },
18 |
19 | computed: {
20 |
21 | isConfirmed: function () {
22 | return this.currentAccount ? true : false;
23 | }
24 |
25 | },
26 |
27 | methods: {
28 |
29 | // Privileges checking
30 |
31 | isAdmin: function () {
32 | return this.isConfirmed && this.currentAccount.isAdmin;
33 | },
34 |
35 | canAccessRoute: function (routeName) {
36 | let routeLevel = config.routePermissions[routeName];
37 | if (
38 | // 0 means guests can access
39 | !routeLevel ||
40 |
41 | // level is > 0, user with at least matching level must be set
42 | (this.isConfirmed && routeLevel <= this.currentAccount.accessLevel)
43 | ) {
44 | return true;
45 | }
46 | return false;
47 | },
48 |
49 |
50 |
51 | // Log in or out
52 |
53 | unsetCurrentAccount: function () {
54 | if (this.currentAccount) {
55 | this.currentAccount = null;
56 | }
57 | },
58 |
59 | confirmAuthentication: function (id) {
60 | this.isLoading = true;
61 |
62 | // Return promise
63 | let service = this;
64 | return api.getAuth(
65 |
66 | // Success callback
67 | function (response) {
68 | // console.log('auth service: confirmed authentication OK', response);
69 | service.currentAccount = response.data.account;
70 | },
71 |
72 | // Error callback
73 | function (error) {
74 | console.log('auth service: ERROR confirming authentication', error);
75 | },
76 |
77 | // Always callback
78 | function () {
79 | service.isLoading = false;
80 | }
81 |
82 | );
83 |
84 | }
85 |
86 | },
87 |
88 | created: function () {
89 | this.confirmAuthentication();
90 | }
91 |
92 | });
93 |
--------------------------------------------------------------------------------
/src/styles/utilities-base/utility-pad.scss:
--------------------------------------------------------------------------------
1 |
2 | @import '~@shared-styles';
3 |
4 |
5 |
6 | .pad {
7 | @include pad;
8 | }
9 |
10 |
11 |
12 | .pad-horizontal {
13 | @include pad-horizontal;
14 | }
15 | .pad-vertical {
16 | @include pad-vertical;
17 | }
18 |
19 |
20 |
21 | .pad-even {
22 | @include pad-even;
23 | }
24 |
25 | .pad-even-max {
26 | @include pad-even-max;
27 | }
28 |
29 |
30 |
31 | .pad-top {
32 | @include pad-top;
33 | }
34 |
35 | .pad-right {
36 | @include pad-right;
37 | }
38 |
39 | .pad-bottom {
40 | @include pad-bottom;
41 | }
42 |
43 | .pad-left {
44 | @include pad-left;
45 | }
46 |
47 |
48 |
49 | // Tight
50 |
51 | .pad-tight {
52 | @include pad-tight;
53 | }
54 |
55 |
56 |
57 | .pad-tight-horizontal {
58 | @include pad-tight-horizontal;
59 | }
60 | .pad-tight-vertical {
61 | @include pad-tight-vertical;
62 | }
63 |
64 |
65 |
66 | .pad-tight-even {
67 | @include pad-tight-even;
68 | }
69 |
70 | .pad-tight-even-max {
71 | @include pad-tight-even-max;
72 | }
73 |
74 |
75 |
76 | .pad-tight-top {
77 | @include pad-tight-top;
78 | }
79 |
80 | .pad-tight-right {
81 | @include pad-tight-right;
82 | }
83 |
84 | .pad-tight-bottom {
85 | @include pad-tight-bottom;
86 | }
87 |
88 | .pad-tight-left {
89 | @include pad-tight-left;
90 | }
91 |
92 |
93 |
94 | // Loose
95 |
96 | .pad-loose {
97 | @include pad-loose;
98 | }
99 |
100 |
101 |
102 | .pad-loose-horizontal {
103 | @include pad-loose-horizontal;
104 | }
105 |
106 | .pad-loose-vertical {
107 | @include pad-loose-vertical;
108 | }
109 |
110 |
111 |
112 | .pad-loose-even {
113 | @include pad-loose-even;
114 | }
115 |
116 | .pad-loose-even-max {
117 | @include pad-loose-even-max;
118 | }
119 |
120 |
121 |
122 | .pad-loose-top {
123 | @include pad-loose-top;
124 | }
125 |
126 | .pad-loose-right {
127 | @include pad-loose-right;
128 | }
129 |
130 | .pad-loose-bottom {
131 | @include pad-loose-bottom;
132 | }
133 |
134 | .pad-loose-left {
135 | @include pad-loose-left;
136 | }
137 |
--------------------------------------------------------------------------------
/src/components/controls/Click.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
83 |
84 |
85 |
86 |
87 |
88 |
91 |
--------------------------------------------------------------------------------
/src/components/transitions/CustomTransition.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
61 |
62 |
63 |
64 |
65 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/styles/mixins/mixin-hide.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './mixin-viewport';
3 |
4 |
5 |
6 | // Super simple base mixin
7 |
8 | @mixin hide {
9 | display: none;
10 | }
11 |
12 |
13 |
14 | // Print
15 |
16 | @mixin hide-in-print {
17 | @include viewport-print {
18 | display: none;
19 | }
20 | }
21 |
22 | @mixin hide-in-not-print {
23 | @include viewport-not-print {
24 | display: none;
25 | }
26 | }
27 |
28 |
29 |
30 | // Orientation
31 |
32 | @mixin hide-in-landscape {
33 | @include viewport-landscape {
34 | display: none;
35 | }
36 | }
37 |
38 | @mixin hide-in-portrait {
39 | @include viewport-portrait {
40 | display: none;
41 | }
42 | }
43 |
44 |
45 |
46 | // Breakpoints
47 |
48 | @mixin hide-under-tiny {
49 | @include viewport-under-tiny {
50 | display: none;
51 | }
52 | }
53 |
54 | @mixin hide-over-tiny {
55 | @include viewport-over-tiny {
56 | display: none;
57 | }
58 | }
59 |
60 | @mixin hide-under-small {
61 | @include viewport-under-small {
62 | display: none;
63 | }
64 | }
65 |
66 | @mixin hide-over-small {
67 | @include viewport-over-small {
68 | display: none;
69 | }
70 | }
71 |
72 | @mixin hide-under-smallish {
73 | @include viewport-under-smallish {
74 | display: none;
75 | }
76 | }
77 |
78 | @mixin hide-over-smallish {
79 | @include viewport-over-smallish {
80 | display: none;
81 | }
82 | }
83 |
84 | @mixin hide-under-medium {
85 | @include viewport-under-medium {
86 | display: none;
87 | }
88 | }
89 |
90 | @mixin hide-over-medium {
91 | @include viewport-over-medium {
92 | display: none;
93 | }
94 | }
95 |
96 | @mixin hide-under-large {
97 | @include viewport-under-large {
98 | display: none;
99 | }
100 | }
101 |
102 | @mixin hide-over-large {
103 | @include viewport-over-large {
104 | display: none;
105 | }
106 | }
107 |
108 | @mixin hide-under-verylarge {
109 | @include viewport-under-verylarge {
110 | display: none;
111 | }
112 | }
113 |
114 | @mixin hide-over-verylarge {
115 | @include viewport-over-verylarge {
116 | display: none;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/services/menu.js:
--------------------------------------------------------------------------------
1 | // Menu service
2 | // Offers up the main menu structure for the current user that can be used by any component
3 | import Vue from 'vue';
4 | import { filter, isUndefined, merge } from 'lodash';
5 |
6 | import { auth } from '@services';
7 | // import config from '@config';
8 |
9 | export default new Vue({
10 |
11 | data: function () {
12 | return {
13 | links: [
14 |
15 | // Home link
16 | {
17 | label: 'Home',
18 | route: {
19 | name: 'root'
20 | }
21 | },
22 |
23 | // Category link
24 | {
25 | label: 'Posts',
26 | // keepParams: [],
27 | route: {
28 | name: 'posts',
29 | params: {
30 | // page: 1
31 | }
32 | }
33 | }
34 |
35 | ]
36 | };
37 | },
38 |
39 | computed: {
40 |
41 | accessibleLinks: function () {
42 | return filter(this.links, function (link) {
43 | return auth.canAccessRoute(link.route.name) ? true : false;
44 | });
45 | },
46 |
47 | accessibleLinksWithoutRoot: function () {
48 | var links = this.accessibleLinks;
49 |
50 | // Look for root link in all links
51 | for (var i = 0; i < links.length; i++) {
52 | var link = links[i];
53 |
54 | // Match based on the named route name
55 | if (link.route.name === 'root') {
56 |
57 | // Remove link
58 | links.splice(i, 1);
59 |
60 | // Only removing the first root link, since we shouldn't have more
61 | break;
62 | }
63 |
64 | }
65 |
66 | return links;
67 | }
68 |
69 | },
70 |
71 | methods: {
72 |
73 | composeRouterLink: function (link, currentRoute) {
74 | var linkParams = link.route.params ? link.route.params : {};
75 |
76 | var keepParams = {};
77 | if (link.keepParams) {
78 |
79 | // Some URLs might be defined in URL and we want to keep them
80 | for (var i = 0; i < link.keepParams.length; i++) {
81 | var paramName = link.keepParams[i];
82 | if (!isUndefined(currentRoute.params[paramName])) {
83 | keepParams[paramName] = currentRoute.params[paramName];
84 | }
85 | }
86 |
87 | }
88 |
89 | return {
90 | name: link.route.name,
91 | params: merge(keepParams, linkParams)
92 | };
93 |
94 | }
95 |
96 | }
97 |
98 | });
99 |
--------------------------------------------------------------------------------
/spec/util/trimWhitespace.spec.js:
--------------------------------------------------------------------------------
1 | import { trimWhitespace } from '@util';
2 |
3 | // Test cases
4 | // These will be tested at the start, end and in the middle of the string
5 | const whitespaceStrings = {
6 | space: ' ',
7 | spaces: ' ',
8 | tab: '\t',
9 | tabAndSpace1: ' ' + '\t',
10 | tabAndSpace2: ' ' + '\t',
11 | tabAndSpace3: ' ' + '\t' + ' ',
12 | tabAndMultipleSpaces1: ' ' + '\t',
13 | tabAndMultipleSpaces2: ' ' + '\t',
14 | tabAndMultipleSpaces3: ' ' + '\t' + ' ',
15 | tabAndMultipleSpacesAndTabs1: ' ' + '\t',
16 | tabAndMultipleSpacesAndTabs2: ' ' + '\t',
17 | tabAndMultipleSpacesAndTabs3: ' ' + '\t' + ' ',
18 | newline: '\n',
19 | newlines: '\n\n\n'
20 | };
21 |
22 | describe('Util trimWhitespace with trailing whitespace', function () {
23 |
24 | // Expected result is the same for all these test cases
25 | const expectedResult = 'Foooo';
26 |
27 | // Test this with each of the test cases provided above
28 | for (let key in whitespaceStrings) {
29 | it('should trim ' + key, function () {
30 |
31 | // Whitespace is at the end of string
32 | expect(trimWhitespace(expectedResult + whitespaceStrings[key])).to.equal(expectedResult);
33 |
34 | });
35 | }
36 |
37 | });
38 |
39 | describe('Util trimWhitespace with leading whitespace', function () {
40 |
41 | // Expected result is the same for all these test cases
42 | const expectedResult = 'Foooo';
43 |
44 | // Test this with each of the test cases provided above
45 | for (let key in whitespaceStrings) {
46 | it('should trim ' + key, function () {
47 |
48 | // Whitespace is at the start of string
49 | expect(trimWhitespace(whitespaceStrings[key] + expectedResult)).to.equal(expectedResult);
50 |
51 | });
52 | }
53 |
54 | });
55 |
56 | describe('Util trimWhitespace with excess whitespace', function () {
57 |
58 | // Expected result is the same for all these test cases
59 | const partOne = 'Foo';
60 | const partTwo = 'oo';
61 | const expectedResult = partOne + ' ' + partTwo;
62 |
63 | // Test this with each of the test cases provided above
64 | for (let key in whitespaceStrings) {
65 | it('should trim ' + key, function () {
66 |
67 | // Whitespace is injected in the middle
68 | expect(trimWhitespace(partOne + whitespaceStrings[key] + partTwo)).to.equal(expectedResult);
69 |
70 | });
71 | }
72 |
73 | });
74 |
--------------------------------------------------------------------------------
/src/components/counters/Counter.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
78 |
79 |
80 |
81 |
82 | {{ label }}:
83 | {{ valueToRender }}
84 |
85 |
86 |
87 |
88 |
101 |
--------------------------------------------------------------------------------
/tooling/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | // This is a karma config file. For more details see
2 | // http://karma-runner.github.io/0.13/config/configuration-file.html
3 | // we are also using it with karma-webpack
4 | // https://github.com/webpack/karma-webpack
5 |
6 | var webpackConfig = require('../../tooling/webpack.test.conf')
7 |
8 | module.exports = function (config) {
9 | config.set({
10 | // to run in additional browsers:
11 | // 1. install corresponding karma launcher
12 | // http://karma-runner.github.io/0.13/config/browsers.html
13 | // 2. add it to the `browsers` array below.
14 | browsers: [
15 | // 'Chrome',
16 | 'PhantomJS'
17 | ],
18 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
19 | reporters: ['spec', 'coverage', 'html'],
20 | files: [
21 |
22 | // Required to suppress issue with ES6 Promise polyfill missing in PhantomJS
23 | '../../node_modules/babel-polyfill/dist/polyfill.js',
24 |
25 | // Required to suppress weird issues when constructing new instances of objects
26 | '../../node_modules/phantomjs-polyfill-object-assign/object-assign-polyfill.js',
27 |
28 | './index.js'
29 | ],
30 | preprocessors: {
31 | './index.js': ['webpack', 'sourcemap']
32 | },
33 | webpack: webpackConfig,
34 | webpackMiddleware: {
35 | noInfo: true
36 | },
37 | coverageReporter: {
38 | dir: '../../reports',
39 | reporters: [
40 | { type: 'lcov', subdir: '.' },
41 | { type: 'text-summary' }
42 | ]
43 | },
44 | // the default configuration
45 | htmlReporter: {
46 | outputDir: 'reports', // where to put the reports
47 | templatePath: null, // set if you moved jasmine_template.html
48 | focusOnFailures: true, // reports show failures on start
49 | namedFiles: true, // name files instead of creating sub-directories
50 | pageTitle: 'Unit test report', // page title for reports; browser info by default
51 | urlFriendlyName: true, // simply replaces spaces with _ for files/dirs
52 | reportName: 'unit', // report summary filename; browser info by default
53 |
54 | // experimental
55 | preserveDescribeNesting: false, // folded suites stay folded
56 | foldAll: false // reports start folded (only with preserveDescribeNesting)
57 | }
58 |
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/snippets/Spinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
57 |
58 |
59 |
60 |
66 |
71 |
72 |
73 |
74 |
75 |
76 |
115 |
--------------------------------------------------------------------------------
/tooling/custom-config.js:
--------------------------------------------------------------------------------
1 |
2 | // Utility file to normalise custom configuration for tooling
3 | // NOTE: This utility is meant only for Webpack tooling, not application code
4 |
5 | var _ = require('lodash')
6 | var path = require('path')
7 |
8 | // Get base custom project configuration values
9 | var aliases = require('../src/config/config.aliases.js');
10 | var values = require('../src/config/config.base.js');
11 | var devValues = require('../src/config/config.dev.base.js');
12 | if (process.env.NODE_ENV === 'development') {
13 | values = _.merge(values, devValues);
14 | }
15 |
16 | // Path helper
17 | // FIXME: duplicated from webpack.base.conf.js
18 | function resolve (dir) {
19 | return path.join(__dirname, '..', dir)
20 | }
21 |
22 |
23 |
24 | // Base values
25 | var normalized = {
26 |
27 | // No action needed
28 | meta: Object.assign({}, values.meta),
29 | svgSpritePath: values.svgSpritePath,
30 | compileAppIcons: values.compileAppIcons,
31 | appIconPlatforms: values.appIconPlatforms,
32 | robotsTxt: values.robotsTxt,
33 | webAppManifest: values.webAppManifest,
34 |
35 | // Prefix some paths with src
36 | appIconSourceFile: resolve('src/' + values.appIconSourceFile),
37 |
38 | // Basics needed for tooling
39 | customAliases: {
40 | 'vue$': 'vue/dist/vue.esm.js',
41 | }
42 |
43 | };
44 |
45 | // https://github.com/NekR/offline-plugin/blob/master/docs/options.md
46 | if (values.offlineCache && values.offlineCache.enabled) {
47 | normalized.offlineCache = {
48 | caches: 'all',
49 | responseStrategy: values.offlineCache.responseStrategy,
50 | updateStrategy: values.offlineCache.updateStrategy,
51 | externals: []
52 | }
53 | }
54 | // https://github.com/NekR/offline-plugin/blob/master/docs/options.md
55 |
56 | // We use a separate plugin for favicons
57 | normalized.appIconPlatforms.favicons = false;
58 |
59 | // SVGO wants its configuration values in a really weird format
60 | // https://github.com/karify/external-svg-sprite-loader/blob/master/index.js
61 | normalized.svgo = {
62 | plugins: []
63 | };
64 | for (var key in values.svgo) {
65 | var item = {};
66 | item[key] = values.svgo[key];
67 | normalized.svgo.plugins.push(item);
68 | }
69 |
70 | // Treat aliases with resolve helper and merge with other configuration
71 | // This will do '@styles': resolve('src/styles')
72 | for (var key in aliases) {
73 | normalized.customAliases[key] = resolve(aliases[key]);
74 | }
75 |
76 |
77 |
78 | // Export final values
79 | module.exports = normalized;
80 |
--------------------------------------------------------------------------------
/src/components/containers/Page.vue:
--------------------------------------------------------------------------------
1 |
2 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
118 |
--------------------------------------------------------------------------------
/spec/util/getDomainName.spec.js:
--------------------------------------------------------------------------------
1 | import { getDomainName } from '@util';
2 |
3 | // Expected result is the same for all these test cases
4 | const expectedResult = 'github.com';
5 |
6 | // Test cases
7 | const stringsWithDomainName = {
8 | bare: 'github.com',
9 | bareWithProtocol: 'http://github.com',
10 | bareWithProtocolHttps: 'https://github.com',
11 | bareWithProtocolFtp: 'ftp://github.com',
12 | bareWithSlash: 'github.com/foo',
13 | bareWithSlashes: 'github.com/foo/bar',
14 | bareWithQueryParameter: 'github.com?foo',
15 | bareWithQueryParameters: 'github.com?foo&bar',
16 | bareWithQueryParametersAndValues: 'github.com?foo&bar=123',
17 | bareWithSlashesAndQueryParameter: 'github.com/foo?foo',
18 | bareWithSlashesAndQueryParameters: 'github.com/foo?foo&bar',
19 | bareWithSlashesAndQueryParametersAndValues: 'github.com/foo?foo&bar=123',
20 | bareWithProtocolAndSlash: 'http://github.com/foo',
21 | bareWithProtocolAndSlashes: 'http://github.com/foo/bar',
22 | bareWithProtocolAndQueryParameter: 'http://github.com?foo',
23 | bareWithProtocolAndQueryParameters: 'http://github.com?foo&bar',
24 | bareWithProtocolAndQueryParametersAndValues: 'http://github.com?foo&bar=123',
25 | bareWithProtocolAndSlashesAndQueryParameter: 'http://github.com/foo?foo',
26 | bareWithProtocolAndSlashesAndQueryParameters: 'http://github.com/foo?foo&bar',
27 | bareWithProtocolAndSlashesAndQueryParametersAndValues: 'http://github.com/foo?foo&bar=123',
28 | bareWithLeadingSpace: ' github.com',
29 | bareWithTrailingSpace: 'github.com ',
30 | leadingSpaceWithProtocolAndSlashesAndQueryParametersAndValues: ' http://github.com/foo?foo&bar=123'
31 | };
32 |
33 | const stringsWithSubDomainUnchanged = {
34 | oneSubDomain: 'foo.github.com',
35 | twoSubDomains: 'foo.bar.github.com'
36 | };
37 |
38 | describe('Util getDomainName', function () {
39 |
40 | // Test this with each of the test cases provided above
41 | for (let key in stringsWithDomainName) {
42 | it('should extract domain from ' + key, function () {
43 | expect(getDomainName(stringsWithDomainName[key])).to.equal(expectedResult);
44 | });
45 | }
46 |
47 | });
48 |
49 | describe('Util getDomainName with subdomains', function () {
50 |
51 | // Test this with each of the test cases provided above
52 | for (let key in stringsWithSubDomainUnchanged) {
53 | it('should extract domain with subdomain from ' + key, function () {
54 | expect(getDomainName(stringsWithSubDomainUnchanged[key])).to.equal(stringsWithSubDomainUnchanged[key]);
55 | });
56 | }
57 |
58 | });
59 |
--------------------------------------------------------------------------------