├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── LICENSE
├── README.md
├── build
├── build.js
├── check-versions.js
├── dev-client.js
├── dev-server.js
├── sprite_module.conf.js
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
├── webpack.prod.conf.js
└── webpack.test.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── index.html
├── package-lock.json
├── package.json
├── src
├── 404.html
├── App.vue
├── assets
│ ├── android-chrome-192x192.png
│ ├── android-chrome-384x384.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── logo.png
│ ├── manifest.json
│ ├── mstile-150x150.png
│ ├── safari-pinned-tab.svg
│ └── svg
│ │ ├── Android.svg
│ │ ├── App.svg
│ │ ├── Apple.svg
│ │ ├── Calendar.svg
│ │ ├── Clock.svg
│ │ ├── Download.svg
│ │ ├── Heart.svg
│ │ ├── Search.svg
│ │ ├── Settings.svg
│ │ ├── ThumbsUp.svg
│ │ └── Watchface.svg
├── components
│ ├── Home.vue
│ ├── Navbar.vue
│ ├── PageFooter.vue
│ ├── SvgContainer.vue
│ └── pages
│ │ ├── AppDetails.vue
│ │ ├── AppVersions.vue
│ │ ├── AppView.vue
│ │ ├── Author.vue
│ │ ├── Category.vue
│ │ ├── Collection.vue
│ │ ├── Error.vue
│ │ ├── Search.vue
│ │ ├── Settings.vue
│ │ └── widgets
│ │ ├── AppSlider.vue
│ │ ├── AppTitleBar.vue
│ │ ├── CardCollection.vue
│ │ ├── GetAppButton.vue
│ │ ├── HomeSlider.vue
│ │ ├── Pagination.vue
│ │ ├── ScreenshotList.vue
│ │ ├── SingleBanner.vue
│ │ ├── SingleCard.vue
│ │ ├── SingleScreenshot.vue
│ │ ├── TagList.vue
│ │ └── content-loaders
│ │ ├── SingleBanner.vue
│ │ ├── SingleCard.vue
│ │ ├── SingleScreenshotRound.vue
│ │ └── SingleScreenshotSquare.vue
├── css
│ └── _error.scss
├── main.js
├── mixin
│ └── index.js
├── router
│ ├── index.js
│ └── search-router.js
├── services
│ └── index.js
└── store
│ ├── config.js
│ ├── index.js
│ ├── secure.js
│ └── userParameters.js
├── static
├── .gitkeep
├── android-chrome-192x192.png
├── android-chrome-384x384.png
├── apple-touch-icon.png
├── browserconfig.xml
├── css
│ ├── _variables.scss
│ └── font-awesome.min.css
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── manifest.json
├── mstile-150x150.png
└── safari-pinned-tab.svg
└── test
├── e2e
├── custom-assertions
│ └── elementCount.js
├── nightwatch.conf.js
├── runner.js
└── specs
│ └── test.js
└── unit
├── .eslintrc
├── index.js
└── karma.conf.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"],
12 | "env": {
13 | "test": {
14 | "presets": ["env", "stage-2"],
15 | "plugins": ["transform-vue-jsx", "istanbul"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /test/unit/coverage/
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint'
7 | },
8 | env: {
9 | browser: true,
10 | },
11 | extends: [
12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
14 | 'plugin:vue/essential',
15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
16 | 'standard'
17 | ],
18 | // required to lint *.vue files
19 | plugins: [
20 | 'vue'
21 | ],
22 | // add your custom rules here
23 | rules: {
24 | // allow async-await
25 | 'generator-star-spacing': 'off',
26 | // allow debugger during development
27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | /test/unit/coverage/
8 | /test/e2e/reports/
9 | selenium-debug.log
10 |
11 | # Editor directories and files
12 | .idea
13 | .vscode
14 | *.suo
15 | *.ntvs*
16 | *.njsproj
17 | *.sln
18 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Rebble (pebble-dev)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rebble Store for pebble
2 |
3 | The Rebble Store is a Pebble Appstore replacement.
4 |
5 | If you want to contribute join us on the [Pebble Dev Discord server](http://discord.gg/aRUAYFN), then head to `#appstore`.
6 |
7 | This is the Rebble replacement for the Pebble app store. This project is under active development, though the eventual goal is to reach feature parity with the current Pebble smartwatch app store.
8 |
9 | This project is built with [VueJS 2](https://vuejs.org/), with webpack scripts included for debugging, hot-reload, and production builds. More information on the Vue webpack build scripts can be found [here](https://github.com/vuejs-templates/webpack).
10 |
11 | ## Backend/API
12 |
13 | This project has a separate backend/api that's currently written in Python. It can be found [here](https://github.com/pebble-dev/rebble-appstore-api).
14 |
15 | It's not necessary to run the API locally unless also developing for the API. The frontend points to the production API by default.
16 |
17 | ## Installing
18 |
19 | If you want to run a local version you will also need to run the backend.
20 |
21 | ``` bash
22 | # install dependencies
23 | npm install
24 |
25 | # serve with hot reload at localhost:8081
26 | npm run dev
27 |
28 | # build for production with minification
29 | npm run build
30 |
31 | # run unit tests
32 | npm run unit
33 |
34 | # run e2e tests
35 | npm run e2e
36 |
37 | # run all tests
38 | npm test
39 | ```
40 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/build/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 |
--------------------------------------------------------------------------------
/build/dev-server.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 | var config = require('../config')
3 | if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
4 | var path = require('path')
5 | var express = require('express')
6 | var webpack = require('webpack')
7 | var opn = require('opn')
8 | var proxyMiddleware = require('http-proxy-middleware')
9 | var webpackConfig = process.env.NODE_ENV === 'testing'
10 | ? require('./webpack.prod.conf')
11 | : require('./webpack.dev.conf')
12 |
13 | // default port where dev server listens for incoming traffic
14 | var port = process.env.PORT || config.dev.port
15 | // Define HTTP proxies to your custom API backend
16 | // https://github.com/chimurai/http-proxy-middleware
17 | var proxyTable = config.dev.proxyTable
18 |
19 | var app = express()
20 | var compiler = webpack(webpackConfig)
21 |
22 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
23 | publicPath: webpackConfig.output.publicPath,
24 | stats: {
25 | colors: true,
26 | chunks: false
27 | }
28 | })
29 |
30 | var hotMiddleware = require('webpack-hot-middleware')(compiler)
31 | // force page reload when html-webpack-plugin template changes
32 | compiler.plugin('compilation', function (compilation) {
33 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
34 | hotMiddleware.publish({ action: 'reload' })
35 | cb()
36 | })
37 | })
38 |
39 | // proxy api requests
40 | Object.keys(proxyTable).forEach(function (context) {
41 | var options = proxyTable[context]
42 | if (typeof options === 'string') {
43 | options = { target: options }
44 | }
45 | app.use(proxyMiddleware(context, options))
46 | })
47 |
48 | // handle fallback for HTML5 history API
49 | app.use(require('connect-history-api-fallback')())
50 |
51 | // serve webpack bundle output
52 | app.use(devMiddleware)
53 |
54 | // enable hot-reload and state-preserving
55 | // compilation error display
56 | app.use(hotMiddleware)
57 |
58 | // serve pure static assets
59 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
60 | app.use(staticPath, express.static('./static'))
61 |
62 | module.exports = app.listen(port, function (err) {
63 | if (err) {
64 | console.log(err)
65 | return
66 | }
67 | var uri = 'http://localhost:' + port
68 | console.log('Listening at ' + uri + '\n')
69 |
70 | // when env is testing, don't need open it
71 | if (process.env.NODE_ENV !== 'testing') {
72 | opn(uri)
73 | }
74 | })
75 |
--------------------------------------------------------------------------------
/build/sprite_module.conf.js:
--------------------------------------------------------------------------------
1 | import BrowserSprite from 'svg-baker-runtime/browser-sprite';
2 | import domready from 'domready'
3 |
4 | const sprite = new BrowserSprite();
5 | domready(() => sprite.mount('#svgContainer'))
6 |
7 | export default sprite;
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass').concat(
64 | {
65 | loader: 'sass-resources-loader',
66 | options: {
67 | resources: [path.resolve(__dirname, '../static/css/_variables.scss')]
68 | }
69 | }
70 | ),
71 | stylus: generateLoaders('stylus'),
72 | styl: generateLoaders('stylus')
73 | }
74 | }
75 |
76 | // Generate loaders for standalone style files (outside of .vue)
77 | exports.styleLoaders = function (options) {
78 | const output = []
79 | const loaders = exports.cssLoaders(options)
80 |
81 | for (const extension in loaders) {
82 | const loader = loaders[extension]
83 | output.push({
84 | test: new RegExp('\\.' + extension + '$'),
85 | use: loader
86 | })
87 | }
88 |
89 | return output
90 | }
91 |
92 | exports.createNotifierCallback = () => {
93 | const notifier = require('node-notifier')
94 |
95 | return (severity, errors) => {
96 | if (severity !== 'error') return
97 |
98 | const error = errors[0]
99 | const filename = error.file && error.file.split('!').pop()
100 |
101 | notifier.notify({
102 | title: packageConfig.name,
103 | message: severity + ': ' + error.name,
104 | subtitle: filename || '',
105 | icon: path.join(__dirname, 'logo.png')
106 | })
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 |
8 | function resolve (dir) {
9 | return path.join(__dirname, '..', dir)
10 | }
11 |
12 | const createLintingRule = () => ({
13 | test: /\.(js|vue)$/,
14 | loader: 'eslint-loader',
15 | enforce: 'pre',
16 | include: [resolve('src'), resolve('test')],
17 | options: {
18 | formatter: require('eslint-friendly-formatter'),
19 | emitWarning: !config.dev.showEslintErrorsInOverlay
20 | }
21 | })
22 |
23 | module.exports = {
24 | context: path.resolve(__dirname, '../'),
25 | entry: {
26 | app: './src/main.js'
27 | },
28 | output: {
29 | path: config.build.assetsRoot,
30 | filename: '[name].js',
31 | publicPath: process.env.NODE_ENV === 'production'
32 | ? config.build.assetsPublicPath
33 | : config.dev.assetsPublicPath
34 | },
35 | resolve: {
36 | extensions: ['.js', '.vue', '.json'],
37 | alias: {
38 | 'vue$': 'vue/dist/vue.esm.js',
39 | '@': resolve('src'),
40 | }
41 | },
42 | module: {
43 | rules: [
44 | ...(config.dev.useEslint ? [createLintingRule()] : []),
45 | {
46 | test: /\.scss$/,
47 | use: [
48 | {
49 | loader: "style-loader" // creates style nodes from JS strings
50 | },
51 | {
52 | loader: "css-loader" // translates CSS into CommonJS
53 | },
54 | {
55 | loader: "sass-loader" // compiles Sass to CSS
56 | }
57 | ]
58 | },
59 | {
60 | test: /\.vue$/,
61 | loader: 'vue-loader',
62 | options: vueLoaderConfig
63 | },
64 | {
65 | test: /\.js$/,
66 | loader: 'babel-loader',
67 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
68 | },
69 | {
70 | test: /\.(svg)$/,
71 | loader: 'svg-sprite-loader',
72 | include: [resolve('src/assets/svg')],
73 | options: {
74 | symbolId: 'icon[name]',
75 | spriteModule: resolve('build/sprite_module.conf.js')
76 | }
77 | },
78 | {
79 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
80 | exclude: [resolve('src/assets/svg')],
81 | use:[
82 | 'file-loader',
83 | ]
84 | },
85 | {
86 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
87 | loader: 'url-loader',
88 | options: {
89 | limit: 10000,
90 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
91 | }
92 | },
93 | {
94 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
95 | loader: 'url-loader',
96 | options: {
97 | limit: 10000,
98 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
99 | }
100 | }
101 | ]
102 | },
103 | node: {
104 | // prevent webpack from injecting useless setImmediate polyfill because Vue
105 | // source contains it (although only uses it if it's native).
106 | setImmediate: false,
107 | // prevent webpack from injecting mocks to Node native modules
108 | // that does not make sense for the client
109 | dgram: 'empty',
110 | fs: 'empty',
111 | net: 'empty',
112 | tls: 'empty',
113 | child_process: 'empty'
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const path = require('path')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
11 | const portfinder = require('portfinder')
12 |
13 | const HOST = process.env.HOST
14 | const PORT = process.env.PORT && Number(process.env.PORT)
15 |
16 | const devWebpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
19 | },
20 | // cheap-module-eval-source-map is faster for development
21 | devtool: config.dev.devtool,
22 |
23 | // these devServer options should be customized in /config/index.js
24 | devServer: {
25 | clientLogLevel: 'warning',
26 | historyApiFallback: {
27 | rewrites: [
28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
29 | ],
30 | },
31 | hot: true,
32 | contentBase: false, // since we use CopyWebpackPlugin.
33 | compress: true,
34 | host: HOST || config.dev.host,
35 | port: PORT || config.dev.port,
36 | open: config.dev.autoOpenBrowser,
37 | overlay: config.dev.errorOverlay
38 | ? { warnings: false, errors: true }
39 | : false,
40 | publicPath: config.dev.assetsPublicPath,
41 | proxy: config.dev.proxyTable,
42 | quiet: true, // necessary for FriendlyErrorsPlugin
43 | watchOptions: {
44 | poll: config.dev.poll,
45 | }
46 | },
47 | plugins: [
48 | new webpack.DefinePlugin({
49 | 'process.env': require('../config/dev.env')
50 | }),
51 | new webpack.HotModuleReplacementPlugin(),
52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
53 | new webpack.NoEmitOnErrorsPlugin(),
54 | // https://github.com/ampedandwired/html-webpack-plugin
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html',
58 | inject: true
59 | }),
60 | // copy custom static assets
61 | new CopyWebpackPlugin([
62 | {
63 | from: path.resolve(__dirname, '../static'),
64 | to: config.dev.assetsSubDirectory,
65 | ignore: ['.*']
66 | }
67 | ])
68 | ]
69 | })
70 |
71 | module.exports = new Promise((resolve, reject) => {
72 | portfinder.basePort = process.env.PORT || config.dev.port
73 | portfinder.getPort((err, port) => {
74 | if (err) {
75 | reject(err)
76 | } else {
77 | // publish the new Port, necessary for e2e tests
78 | process.env.PORT = port
79 | // add port to devServer config
80 | devWebpackConfig.devServer.port = port
81 |
82 | // Add FriendlyErrorsPlugin
83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
84 | compilationSuccessInfo: {
85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
86 | },
87 | onErrors: config.dev.notifyOnErrors
88 | ? utils.createNotifierCallback()
89 | : undefined
90 | }))
91 |
92 | resolve(devWebpackConfig)
93 | }
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 |
14 | const env = process.env.NODE_ENV === 'testing'
15 | ? require('../config/test.env')
16 | : require('../config/prod.env')
17 |
18 | const webpackConfig = merge(baseWebpackConfig, {
19 | module: {
20 | rules: utils.styleLoaders({
21 | sourceMap: config.build.productionSourceMap,
22 | extract: true,
23 | usePostCSS: true
24 | })
25 | },
26 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
27 | output: {
28 | path: config.build.assetsRoot,
29 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
30 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
31 | },
32 | plugins: [
33 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
34 | new webpack.DefinePlugin({
35 | 'process.env': env
36 | }),
37 | new UglifyJsPlugin({
38 | uglifyOptions: {
39 | compress: {
40 | warnings: false
41 | }
42 | },
43 | sourceMap: config.build.productionSourceMap,
44 | parallel: true
45 | }),
46 | // extract css into its own file
47 | new ExtractTextPlugin({
48 | filename: utils.assetsPath('css/[name].[contenthash].css'),
49 | // Setting the following option to `false` will not extract CSS from codesplit chunks.
50 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
51 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
52 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
53 | allChunks: true,
54 | }),
55 | // Compress extracted CSS. We are using this plugin so that possible
56 | // duplicated CSS from different components can be deduped.
57 | new OptimizeCSSPlugin({
58 | cssProcessorOptions: config.build.productionSourceMap
59 | ? { safe: true, map: { inline: false } }
60 | : { safe: true }
61 | }),
62 | // generate dist index.html with correct asset hash for caching.
63 | // you can customize output by editing /index.html
64 | // see https://github.com/ampedandwired/html-webpack-plugin
65 | new HtmlWebpackPlugin({
66 | filename: process.env.NODE_ENV === 'testing'
67 | ? 'index.html'
68 | : config.build.index,
69 | template: 'index.html',
70 | inject: true,
71 | minify: {
72 | removeComments: true,
73 | collapseWhitespace: true,
74 | removeAttributeQuotes: true
75 | // more options:
76 | // https://github.com/kangax/html-minifier#options-quick-reference
77 | },
78 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
79 | chunksSortMode: 'dependency'
80 | }),
81 | // keep module.id stable when vendor modules does not change
82 | new webpack.HashedModuleIdsPlugin(),
83 | // enable scope hoisting
84 | new webpack.optimize.ModuleConcatenationPlugin(),
85 | // split vendor js into its own file
86 | new webpack.optimize.CommonsChunkPlugin({
87 | name: 'vendor',
88 | minChunks (module) {
89 | // any required modules inside node_modules are extracted to vendor
90 | return (
91 | module.resource &&
92 | /\.js$/.test(module.resource) &&
93 | module.resource.indexOf(
94 | path.join(__dirname, '../node_modules')
95 | ) === 0
96 | )
97 | }
98 | }),
99 | // extract webpack runtime and module manifest to its own file in order to
100 | // prevent vendor hash from being updated whenever app bundle is updated
101 | new webpack.optimize.CommonsChunkPlugin({
102 | name: 'manifest',
103 | minChunks: Infinity
104 | }),
105 | // This instance extracts shared chunks from code splitted chunks and bundles them
106 | // in a separate chunk, similar to the vendor chunk
107 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
108 | new webpack.optimize.CommonsChunkPlugin({
109 | name: 'app',
110 | async: 'vendor-async',
111 | children: true,
112 | minChunks: 3
113 | }),
114 |
115 | // copy custom static assets
116 | new CopyWebpackPlugin([
117 | {
118 | from: path.resolve(__dirname, '../static'),
119 | to: config.build.assetsSubDirectory,
120 | ignore: ['.*']
121 | }
122 | ])
123 | ]
124 | })
125 |
126 | if (config.build.productionGzip) {
127 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
128 |
129 | webpackConfig.plugins.push(
130 | new CompressionWebpackPlugin({
131 | asset: '[path].gz[query]',
132 | algorithm: 'gzip',
133 | test: new RegExp(
134 | '\\.(' +
135 | config.build.productionGzipExtensions.join('|') +
136 | ')$'
137 | ),
138 | threshold: 10240,
139 | minRatio: 0.8
140 | })
141 | )
142 | }
143 |
144 | if (config.build.bundleAnalyzerReport) {
145 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
146 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
147 | }
148 |
149 | module.exports = webpackConfig
150 |
--------------------------------------------------------------------------------
/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // This is the webpack config used for unit tests.
3 |
4 | const utils = require('./utils')
5 | const webpack = require('webpack')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 |
9 | const webpackConfig = merge(baseWebpackConfig, {
10 | // use inline sourcemap for karma-sourcemap-loader
11 | module: {
12 | rules: utils.styleLoaders()
13 | },
14 | devtool: '#inline-source-map',
15 | resolveLoader: {
16 | alias: {
17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
19 | 'scss-loader': 'sass-loader'
20 | }
21 | },
22 | plugins: [
23 | new webpack.DefinePlugin({
24 | 'process.env': require('../config/test.env')
25 | })
26 | ]
27 | })
28 |
29 | // no need for app entry during tests
30 | delete webpackConfig.entry
31 |
32 | module.exports = webpackConfig
33 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: '0.0.0.0', // can be overwritten by process.env.HOST
17 | port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: true,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'cheap-module-eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | cssSourceMap: true
44 | },
45 |
46 | build: {
47 | // Template for index.html
48 | index: path.resolve(__dirname, '../dist/index.html'),
49 |
50 | // Paths
51 | assetsRoot: path.resolve(__dirname, '../dist'),
52 | assetsSubDirectory: 'static',
53 | assetsPublicPath: '/',
54 |
55 | /**
56 | * Source Maps
57 | */
58 |
59 | productionSourceMap: true,
60 | // https://webpack.js.org/configuration/devtool/#production
61 | devtool: '#source-map',
62 |
63 | // Gzip off by default as many popular static hosts such as
64 | // Surge or Netlify already gzip all static assets for you.
65 | // Before setting to `true`, make sure to:
66 | // npm install --save-dev compression-webpack-plugin
67 | productionGzip: false,
68 | productionGzipExtensions: ['js', 'css'],
69 |
70 | // Run the build command with an extra argument to
71 | // View the bundle analyzer report after build finishes:
72 | // `npm run build --report`
73 | // Set to `true` or `false` to always turn it on or off
74 | bundleAnalyzerReport: process.env.npm_config_report
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const devEnv = require('./dev.env')
4 |
5 | module.exports = merge(devEnv, {
6 | NODE_ENV: '"testing"'
7 | })
8 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Rebble App Store
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rebble-app-store",
3 | "version": "1.0.0-beta.3",
4 | "description": "Rebble Store, a pebble app store replacement.",
5 | "author": "Rebble Web Services Team",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
11 | "e2e": "node test/e2e/runner.js",
12 | "test": "npm run unit && npm run e2e",
13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
14 | "build": "node build/build.js"
15 | },
16 | "dependencies": {
17 | "algoliasearch": "^3.33.0",
18 | "bootstrap-vue": "^2.0.0-rc.27",
19 | "es6-promise": "^4.2.5",
20 | "vue": "^2.6.10",
21 | "vue-cookie": "^1.1.4",
22 | "vue-dragscroll": "^1.10.0",
23 | "vue-instantsearch": "^2.3.0",
24 | "vue-router": "^3.0.7",
25 | "vuex": "^3.1.1",
26 | "vuex-pathify": "^1.2.4"
27 | },
28 | "devDependencies": {
29 | "autoprefixer": "^7.1.2",
30 | "babel-core": "^6.26.3",
31 | "babel-eslint": "^8.2.1",
32 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
33 | "babel-loader": "^7.1.1",
34 | "babel-plugin-istanbul": "^4.1.6",
35 | "babel-plugin-syntax-jsx": "^6.18.0",
36 | "babel-plugin-transform-runtime": "^6.22.0",
37 | "babel-plugin-transform-vue-jsx": "^3.7.0",
38 | "babel-preset-env": "^1.7.0",
39 | "babel-preset-stage-2": "^6.22.0",
40 | "babel-register": "^6.22.0",
41 | "chai": "^4.2.0",
42 | "chalk": "^2.4.1",
43 | "chromedriver": "^2.44.1",
44 | "copy-webpack-plugin": "^4.6.0",
45 | "cross-env": "^5.2.0",
46 | "cross-spawn": "^5.0.1",
47 | "css-loader": "^0.28.0",
48 | "domready": "^1.0.8",
49 | "eslint": "^4.15.0",
50 | "eslint-config-standard": "^10.2.1",
51 | "eslint-friendly-formatter": "^3.0.0",
52 | "eslint-loader": "^1.7.1",
53 | "eslint-plugin-import": "^2.14.0",
54 | "eslint-plugin-node": "^5.2.0",
55 | "eslint-plugin-promise": "^3.4.0",
56 | "eslint-plugin-standard": "^3.0.1",
57 | "eslint-plugin-vue": "^4.0.0",
58 | "extract-text-webpack-plugin": "^3.0.0",
59 | "file-loader": "^1.1.4",
60 | "friendly-errors-webpack-plugin": "^1.7.0",
61 | "html-webpack-plugin": "^2.30.1",
62 | "inject-loader": "^3.0.0",
63 | "karma": "^1.4.1",
64 | "karma-coverage": "^1.1.2",
65 | "karma-mocha": "^1.3.0",
66 | "karma-phantomjs-launcher": "^1.0.2",
67 | "karma-phantomjs-shim": "^1.4.0",
68 | "karma-sinon-chai": "^1.3.1",
69 | "karma-sourcemap-loader": "^0.3.7",
70 | "karma-spec-reporter": "0.0.31",
71 | "karma-webpack": "^2.0.2",
72 | "mocha": "^3.2.0",
73 | "nightwatch": "^0.9.12",
74 | "node-notifier": "^5.3.0",
75 | "node-sass": "^4.11.0",
76 | "optimize-css-assets-webpack-plugin": "^3.2.0",
77 | "ora": "^1.2.0",
78 | "phantomjs-prebuilt": "^2.1.14",
79 | "portfinder": "^1.0.20",
80 | "postcss-import": "^11.0.0",
81 | "postcss-loader": "^2.0.8",
82 | "postcss-url": "^7.2.1",
83 | "rimraf": "^2.6.0",
84 | "sass-loader": "^6.0.6",
85 | "sass-resources-loader": "^1.3.2",
86 | "selenium-server": "^3.141.59",
87 | "semver": "^5.6.0",
88 | "shelljs": "^0.7.6",
89 | "sinon": "^4.0.0",
90 | "sinon-chai": "^2.8.0",
91 | "svg-sprite-loader": "^3.9.0",
92 | "uglifyjs-webpack-plugin": "^1.1.1",
93 | "url-loader": "^0.5.8",
94 | "vue-content-loading": "^1.5.3",
95 | "vue-images-loaded": "^1.1.2",
96 | "vue-loader": "^13.3.0",
97 | "vue-resource": "^1.5.1",
98 | "vue-style-loader": "^3.0.1",
99 | "vue-template-compiler": "^2.5.21",
100 | "webpack": "^3.6.0",
101 | "webpack-bundle-analyzer": "^2.9.0",
102 | "webpack-dev-server": "^2.9.1",
103 | "webpack-merge": "^4.1.5"
104 | },
105 | "engines": {
106 | "node": ">= 6.0.0",
107 | "npm": ">= 3.0.0"
108 | },
109 | "browserslist": [
110 | "> 1%",
111 | "last 2 versions",
112 | "not ie <= 8"
113 | ]
114 | }
115 |
--------------------------------------------------------------------------------
/src/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Rebble Store
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
93 |
94 |
95 |
96 |
97 |
Our pet rock has lost your page, sorry about that
98 |
99 |
131 |
132 |
We're getting the following message {{ 404 }}
133 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
69 |
70 |
238 |
--------------------------------------------------------------------------------
/src/assets/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/android-chrome-192x192.png
--------------------------------------------------------------------------------
/src/assets/android-chrome-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/android-chrome-384x384.png
--------------------------------------------------------------------------------
/src/assets/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/assets/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #373a3c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/favicon-16x16.png
--------------------------------------------------------------------------------
/src/assets/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/favicon-32x32.png
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Rebble Store",
3 | "icons": [
4 | {
5 | "src": "\/assets\/android-chrome-192x192.png",
6 | "sizes": "192x192",
7 | "type": "image\/png"
8 | },
9 | {
10 | "src": "\/assets\/android-chrome-384x384.png",
11 | "sizes": "384x384",
12 | "type": "image\/png"
13 | }
14 | ],
15 | "theme_color": "#ffffff",
16 | "display": "standalone"
17 | }
18 |
--------------------------------------------------------------------------------
/src/assets/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/src/assets/mstile-150x150.png
--------------------------------------------------------------------------------
/src/assets/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
27 |
--------------------------------------------------------------------------------
/src/assets/svg/Android.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/svg/App.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/svg/Apple.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/svg/Calendar.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/assets/svg/Clock.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/svg/Download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/Heart.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/svg/Search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/Settings.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/ThumbsUp.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/svg/Watchface.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
72 |
73 |
83 |
--------------------------------------------------------------------------------
/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ❮
6 |
7 |
8 | Rebble Store
9 |
10 | for
11 | pebble
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 | Watchfaces
35 |
36 |
37 |
38 |
41 | Apps
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
88 |
89 |
227 |
--------------------------------------------------------------------------------
/src/components/PageFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
24 |
25 |
73 |
--------------------------------------------------------------------------------
/src/components/SvgContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
35 |
36 |
37 |
38 |
46 |
47 |
222 |
--------------------------------------------------------------------------------
/src/components/pages/AppDetails.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Description
7 |
{{ app.description }}
8 |
29 |
30 |
31 | Version Information
32 |
33 |
34 |
35 |
36 | Website Link
37 |
38 |
39 |
43 |
44 | Source code
45 |
46 |
47 | More From This Developer
48 |
49 |
50 |
51 | Download .pbw
52 |
53 |
54 |
55 |
56 |
57 |
58 |
86 |
87 |
93 |
--------------------------------------------------------------------------------
/src/components/pages/AppVersions.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Version {{ changelog.version }}
{{ changelog.published_date | formatDate }}
5 |
{{ changelog.release_notes }}
6 |
7 |
8 |
9 |
10 |
40 |
41 |
49 |
--------------------------------------------------------------------------------
/src/components/pages/AppView.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
47 |
48 |
154 |
--------------------------------------------------------------------------------
/src/components/pages/Author.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Apps by: {{ page.data[0].author }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
56 |
57 |
59 |
--------------------------------------------------------------------------------
/src/components/pages/Category.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{id | readable-name}}
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
70 |
71 |
76 |
--------------------------------------------------------------------------------
/src/components/pages/Collection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Collection: {{slug | readable-name}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
65 |
66 |
68 |
--------------------------------------------------------------------------------
/src/components/pages/Error.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Our pet rock has lost your page, sorry about that
8 |
9 |
31 |
32 |
We're getting the following message {{ 404 }}
33 |
34 |
35 | Back to home
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
58 |
59 |
100 |
--------------------------------------------------------------------------------
/src/components/pages/Search.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 | No results were found.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
94 |
95 |
131 |
--------------------------------------------------------------------------------
/src/components/pages/Settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Pebble Type
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
23 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/AppSlider.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
33 |
34 |
63 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/AppTitleBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
![]()
6 |
7 |
{{ app.title }}
8 | {{ app.author }}
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
41 |
42 |
43 |
44 |
132 |
133 |
265 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/CardCollection.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
46 |
47 |
102 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/GetAppButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 | GET
8 |
9 |
15 |
16 | "{{app.title}}" is requesting access to the following services.
17 |
18 | {{ item }}
19 |
20 | If you tap on accept you will grant "{{app.title}}" access to read and use your data.
21 |
22 |
23 | "{{app.title}}" requires a companion a to be used.
24 |
25 |
26 |
27 |
28 |
120 |
121 |
123 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/HomeSlider.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
35 |
36 |
65 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/Pagination.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
35 |
36 |
38 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/ScreenshotList.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
30 |
31 |
57 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/SingleBanner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![Banner]()
4 |
5 |
6 |
7 |
8 |
41 |
42 |
58 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/SingleCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![App Icon]()
7 |
8 |
{{ card.title }}
9 |
10 |
11 |
14 | {{ card.hearts }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
81 |
82 |
167 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/SingleScreenshot.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![Screenshot]()
4 |
5 |
6 |
7 |
8 |
9 |
43 |
44 |
79 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/TagList.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
16 |
17 |
41 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/content-loaders/SingleBanner.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/content-loaders/SingleCard.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/content-loaders/SingleScreenshotRound.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/pages/widgets/content-loaders/SingleScreenshotSquare.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/css/_error.scss:
--------------------------------------------------------------------------------
1 | // _error.scss
2 | // Error page styles
3 |
4 |
5 | .page-error {
6 | // Calculate top margin 58 px ar of the navbar
7 | margin-top: 40px + 58px;
8 | min-height: 400px;
9 | height: 80vh;
10 | display: flex;
11 | flex-direction: column;
12 | max-width: 36rem;
13 | margin-left: auto;
14 | margin-right: auto;
15 | align-items: center;
16 | justify-content: center;
17 |
18 | .pet-rock-pebble {
19 | // Mascot svg
20 | margin-top: 3rem;
21 | margin-bottom: 3rem;
22 | }
23 |
24 | .page-error_buttons {
25 | // Minimal style changes to buttons
26 | margin-top: 3rem;
27 | .btn-outline-pebble {
28 | &:hover {
29 | color: #f4f3f4;
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueResource from 'vue-resource'
3 | import BootstrapVue from 'bootstrap-vue'
4 |
5 | import 'bootstrap/dist/css/bootstrap.css'
6 | import 'bootstrap-vue/dist/bootstrap-vue.css'
7 |
8 | import App from './App'
9 | import router from './router'
10 | import store from './store'
11 | import mixin from './mixin'
12 |
13 | import InstantSearch from 'vue-instantsearch'
14 | import VueCookie from 'vue-cookie'
15 |
16 | import CardCollection from './components/pages/widgets/CardCollection'
17 |
18 | Vue.filter('formatDate', function (d) {
19 | let date = new Date(d)
20 | if (date) {
21 | return date.getFullYear() + '-' + ((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '-' + (date.getDate() >= 10 ? date.getDate() : ('0' + date.getDate()))
22 | }
23 | })
24 |
25 | Vue.filter('capitalize', function (value) {
26 | if (!value) return ''
27 | value = value.toString()
28 | return value.charAt(0).toUpperCase() + value.slice(1)
29 | })
30 |
31 | Vue.filter('readable-name', function (value) {
32 | if (!value) return ''
33 | value = value.toString()
34 | return value[0].toUpperCase() + value.replace(new RegExp('-', 'g'), ' ').substring(1)
35 | })
36 |
37 | Vue.mixin({
38 | methods: {
39 | buildResourceUrl: mixin.buildResourceUrl,
40 | setTitle: mixin.setTitle,
41 | openExternal: mixin.openExternal
42 | }
43 | })
44 |
45 | Vue.use(VueResource)
46 | Vue.use(BootstrapVue)
47 |
48 | Vue.use(InstantSearch)
49 | Vue.use(VueCookie)
50 |
51 | Vue.component('card-collection', CardCollection)
52 |
53 | /* eslint-disable no-new */
54 | new Vue({
55 | el: '#app',
56 | router,
57 | store,
58 | beforeCreate () {
59 | this.$store.commit('userParameters/INIT', null, { root: true })
60 | this.$store.commit('secure/INIT', null, { root: true })
61 | store.subscribe((mutation, state) => {
62 | if (mutation.type.substr(0, 15) === 'userParameters/') {
63 | // Only save user parameters when needed
64 | localStorage.setItem('rebbleUserParameters', JSON.stringify(state.userParameters))
65 | }
66 | if (mutation.type.substr(0, 23) === 'secure/SET_ACCESS_TOKEN') {
67 | // Only save user parameters when needed
68 | this.$cookie.set('access_token', state.secure.accessToken)
69 | }
70 | })
71 | },
72 | render: h => h(App)
73 | })
74 |
--------------------------------------------------------------------------------
/src/mixin/index.js:
--------------------------------------------------------------------------------
1 | import { Native } from '../services'
2 | import { hardwareEnum } from '../store/userParameters'
3 |
4 | const mixins = {
5 | buildResourceUrl (resource) {
6 | return `${this.$store.state.config.backendUrl}/${resource}?platform=${this.$store.state.userParameters.platform}${this.$store.state.userParameters.hardware !== hardwareEnum.all ? `&hardware=${this.$store.state.userParameters.hardware}&filter_hardware=true` : ''}`
7 | },
8 | setTitle (title = '') {
9 | document.title = title === '' ? 'Rebble Store' : `${title} | Rebble Store`
10 | if (this.$store.state.userParameters.inApp === true) {
11 | Native.send('setNavBarTitle', { title: title })
12 | }
13 | },
14 | openExternal (url) {
15 | if (this.$store.state.userParameters.inApp === true) {
16 | Native.send('openURL', {
17 | url: url
18 | })
19 | } else {
20 | window.open(url, '_blank')
21 | }
22 | }
23 | }
24 | export default mixins
25 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs'
2 | import Vue from 'vue'
3 | import Router from 'vue-router'
4 | import Home from '@/components/Home'
5 | import Category from '@/components/pages/Category'
6 | import AppView from '@/components/pages/AppView'
7 | import AppDetails from '@/components/pages/AppDetails'
8 | import AppVersions from '@/components/pages/AppVersions'
9 | import Author from '@/components/pages/Author'
10 | import Search from '@/components/pages/Search'
11 | import Collection from '@/components/pages/Collection'
12 | import Settings from '@/components/pages/Settings'
13 | import Error from '@/components/pages/Error'
14 |
15 | Vue.use(Router)
16 |
17 | const routes = [
18 | {path: '', redirect: '/faces'},
19 | {
20 | path: '/category/:id/:sort/:page',
21 | component: Category
22 | },
23 | {
24 | path: '/category/:id',
25 | redirect: '/category/:id/hearts/1'
26 | },
27 | {
28 | path: '/category/:id/:sort',
29 | redirect: '/category/:id/:sort/1'
30 | },
31 | {
32 | path: '/app/:id',
33 | component: AppView,
34 | children: [
35 | {
36 | path: '',
37 | component: AppDetails
38 | },
39 | {
40 | path: 'versions',
41 | component: AppVersions
42 | }
43 | ]
44 | },
45 | {path: '/author/:id', redirect: '/author/:id/1'},
46 | {path: '/author/:id/:page', component: Author},
47 | {path: '/settings', component: Settings},
48 | {path: '/:type', component: Home},
49 | {
50 | path: '/:type/search',
51 | component: Search,
52 | props: true
53 | },
54 | {path: '/:type/:slug', redirect: '/:type/:slug/1'},
55 | {path: '/:type/:slug/:page', component: Collection},
56 | {path: '*', component: Error}
57 | ]
58 |
59 | export default new Router({
60 | mode: 'history',
61 | routes: routes,
62 | parseQuery (query) {
63 | return qs.parse(query)
64 | },
65 | stringifyQuery (query) {
66 | const result = qs.stringify(query)
67 |
68 | return result ? '?' + result : ''
69 | }
70 | })
71 |
--------------------------------------------------------------------------------
/src/router/search-router.js:
--------------------------------------------------------------------------------
1 | import vueRouter from './index'
2 |
3 | export const searchRouting = {
4 | router: {
5 | read () {
6 | return vueRouter.currentRoute.query
7 | },
8 | write (routeState) {
9 | vueRouter.push({
10 | query: routeState
11 | })
12 | },
13 | createURL (routeState) {
14 | return vueRouter.resolve({
15 | query: routeState
16 | }).href
17 | },
18 | onUpdate (cb) {
19 | this._onPopState = ({state}) => {
20 | const routeState = state
21 | // at initial load, the state is read from the URL without
22 | // update. Therefore the state object is not there. In this
23 | // case we fallback and read the URL.
24 | if (!routeState) {
25 | cb(this.read())
26 | } else {
27 | cb(routeState)
28 | }
29 | }
30 | window.addEventListener('popstate', this._onPopState)
31 | },
32 | dispose () {
33 | window.removeEventListener('popstate', this._onPopState)
34 | this.write()
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/services/index.js:
--------------------------------------------------------------------------------
1 | import store from '../store'
2 | import router from '../router'
3 |
4 | class NativeService {
5 | constructor () {
6 | this.callbacks = []
7 | this.callbackId = 0
8 | this.methods = ['setNavBarTitle', 'openURL', 'addToLocker', 'loadAppToDeviceAndLocker', 'promptUserForAddToLockerOrLoad', 'getAppsFromLocker', 'removeFromLocker', 'isAppInLocker', 'unloadAppFromPebble', 'getLoadedAppsFromPebble', 'tryWatchface', 'isConnected', 'closeScreen', 'skipStep', 'bulkLoadAndClose', 'setVisibleApp', 'refreshAccessToken']
9 | window.PebbleBridge = this
10 | }
11 |
12 | send (methodName, args, responseCallback, sendCallback) {
13 | window.setTimeout(() => {
14 | if (typeof methodName !== 'string') return this._sendError('Native: methodName is not an object', sendCallback)
15 | if (!~this.methods.indexOf(methodName)) return this._sendError(`Native: ${methodName} is not in list of known methods`, sendCallback)
16 | if (typeof args !== 'object') return this._sendError('Native: args is not an object', sendCallback)
17 | // if (config.IS_BROWSER) return void $log.debug('Native: ' + methodName + ' not available in browser');
18 | var _callbackId = -1
19 | if (typeof responseCallback === 'function') {
20 | this.callbacks.push(responseCallback)
21 | _callbackId = this.callbackId
22 | this.callbackId = this.callbackId + 1
23 | } else {
24 | responseCallback && this._sendError('Native: callback is not a function')
25 | }
26 | var uri = this._buildURI(methodName, _callbackId, args)
27 | this._executeSend(uri)
28 | if (typeof sendCallback === 'function') {
29 | sendCallback(null, uri)
30 | }
31 | })
32 | }
33 |
34 | _executeSend (uri) {
35 | let iframe = document.createElement('iframe')
36 | iframe.setAttribute('src', uri)
37 | iframe.setAttribute('height', '1px')
38 | iframe.setAttribute('width', '1px')
39 | document.documentElement.appendChild(iframe)
40 | iframe.parentNode.removeChild(iframe)
41 | iframe = null
42 | }
43 |
44 | _sendError (err, callback) {
45 | console.error(err)
46 | callback(err)
47 | }
48 |
49 | _buildURI (methodName, callbackId, args) {
50 | let msg = this._encodeMsg(methodName, callbackId, args)
51 | let protocol = 'pebble-method-call-js-frame://'
52 | let queryCharacter = store.state.userParameters.platform === 'ios' ? '?' : ''
53 | let uri = protocol + queryCharacter + 'method=' + methodName + '&args=' + msg
54 | return uri
55 | }
56 |
57 | _encodeMsg (methodName, callbackId, args) {
58 | let msgStringified
59 | let msg = {
60 | methodName: methodName,
61 | callbackId: callbackId,
62 | data: args
63 | }
64 | try {
65 | msgStringified = JSON.stringify(msg)
66 | } catch (e) {
67 | return void console.error('Native: msg cannot be JSON encoded', e)
68 | }
69 | let msgURIEncoded
70 | try {
71 | msgURIEncoded = encodeURIComponent(msgStringified)
72 | } catch (e) {
73 | return void console.error('Native: msg cannot be URI encoded', e)
74 | }
75 | return msgURIEncoded
76 | }
77 |
78 | handleResponse (args) {
79 | if (typeof args !== 'object' && args !== null) return void console.error('Native: args.methodName is not an object')
80 | if (typeof args.data !== 'object') return void console.error('Native: args.data is not an object')
81 | if (typeof args.callbackId !== 'number') return void console.error('Native: args.callbackId is not a number')
82 | if (args.callbackId < 0) return
83 | let callback = this.callbacks[args.callbackId]
84 | delete this.callbacks[args.callbackId]
85 | if (callback && typeof callback === 'function') {
86 | callback(args.data)
87 | } else {
88 | console.error('Native: callback is not a function', callback)
89 | }
90 | }
91 | _reload () {
92 | window.location.reload(true)
93 | }
94 |
95 | handleRequest (args) {
96 | if (typeof args !== 'object') return void console.error('Native: args.methodName is not an object')
97 | if (typeof args.methodName !== 'string') return void console.error('Native: args.methodName is not an object')
98 | switch (args.methodName) {
99 | case 'search':
100 | // let section = args.section || Storage.get('activeSection') || 'watchapps'
101 | // let query = args.query || (Storage.get('searchData-' + section) || {}).query || ''
102 | let section = args.section || 'apps'
103 | let query = args.query || ''
104 | let isNative = !(!args.query && !args.section)
105 | let url = `/${section}/search?page=1&query=${encodeURIComponent(query)}${(isNative ? '&inApp=true' : '')}`
106 | router.push(url)
107 | break
108 | case 'navigate':
109 | router.push(args.url || '/')
110 | break
111 | case 'refresh':
112 | this._reload()
113 | }
114 | }
115 | }
116 |
117 | export const Native = new NativeService()
118 |
--------------------------------------------------------------------------------
/src/store/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | namespaced: true,
3 | state: {
4 | backendUrl: 'https://appstore-api.rebble.io/api/v1',
5 | devPortalBackendUrl: 'https://appstore-api.rebble.io/api/v0',
6 | tosLink: 'https://rebble.io/tos/',
7 | devPortalLink: 'https://rebble.io/submit/',
8 | contactLink: '',
9 | accessToken: null
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import pathify from 'vuex-pathify'
4 | import userParameters from './userParameters'
5 | import config from './config'
6 | import secure from './secure'
7 |
8 | Vue.use(Vuex)
9 |
10 | export default new Vuex.Store({
11 | modules: {
12 | userParameters: userParameters,
13 | config: config,
14 | secure: secure
15 | },
16 | plugins: [pathify.plugin]
17 | })
18 |
--------------------------------------------------------------------------------
/src/store/secure.js:
--------------------------------------------------------------------------------
1 | import VueCookie from 'vue-cookie'
2 | import { make } from 'vuex-pathify'
3 |
4 | const state = {
5 | accessToken: null
6 | }
7 |
8 | export default {
9 | namespaced: true,
10 | state: state,
11 | mutations: {
12 | ...make.mutations(state),
13 | INIT (state) {
14 | if (VueCookie.get('access_token')) {
15 | state.accessToken = VueCookie.get('access_token')
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/store/userParameters.js:
--------------------------------------------------------------------------------
1 | import { make } from 'vuex-pathify'
2 | import { version } from '../../package.json'
3 |
4 | export const platformEnum = {
5 | all: 'all',
6 | ios: 'ios',
7 | android: 'android'
8 | }
9 |
10 | export const hardwareEnum = {
11 | all: 'all',
12 | aplite: 'aplite', // OG Pebble and Pebble Steel
13 | basalt: 'basalt', // Pebble Time and Pebble Time Steel
14 | chalk: 'chalk', // Pebble Round
15 | diorite: 'diorite' // Pebble 2
16 | }
17 |
18 | const state = {
19 | version: '',
20 | platform: platformEnum.all, // either 'android', 'ios', or 'all'
21 | hardware: hardwareEnum.all,
22 | appVersion: '',
23 | inApp: false,
24 | devMode: false
25 | }
26 |
27 | export default {
28 | namespaced: true,
29 | state: state,
30 | mutations: {
31 | ...make.mutations(state),
32 | INIT (state) {
33 | if (localStorage.getItem('rebbleUserParameters')) {
34 | let cacheState = JSON.parse(localStorage.getItem('rebbleUserParameters'))
35 | if (cacheState.version === version) {
36 | Object.assign(state, cacheState)
37 | } else {
38 | state.version = version
39 | }
40 | } else {
41 | state.version = version
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/.gitkeep
--------------------------------------------------------------------------------
/static/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/android-chrome-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/android-chrome-384x384.png
--------------------------------------------------------------------------------
/static/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #373a3c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/static/css/_variables.scss:
--------------------------------------------------------------------------------
1 | // _varaibles.scss
2 | // Store all the variables in here
3 |
4 | // Bootstrap's grid breakpoints
5 | // Use them this way: map-get($grid-breakpoints, sm)
6 | $grid-breakpoints: (
7 | xs: 0,
8 | sm: 576px,
9 | md: 768px,
10 | lg: 992px,
11 | xl: 1200px
12 | );
13 |
14 | // Pebble orange color
15 | $pebble-color: #ff4700;
16 |
17 | // Main Background color (originally a really light gray)
18 | $main-bg-color: #f4f3f4;
19 |
--------------------------------------------------------------------------------
/static/css/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
--------------------------------------------------------------------------------
/static/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/favicon-32x32.png
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/favicon.ico
--------------------------------------------------------------------------------
/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Rebble Store",
3 | "icons": [
4 | {
5 | "src": "\/static\/android-chrome-192x192.png",
6 | "sizes": "192x192",
7 | "type": "image\/png"
8 | },
9 | {
10 | "src": "\/static\/android-chrome-384x384.png",
11 | "sizes": "384x384",
12 | "type": "image\/png"
13 | }
14 | ],
15 | "theme_color": "#ffffff",
16 | "display": "standalone"
17 | }
18 |
--------------------------------------------------------------------------------
/static/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pebble-dev/rebble-store/b4d5ecf61521b5558578deb2e946b1adb94b31ad/static/mstile-150x150.png
--------------------------------------------------------------------------------
/static/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
27 |
--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // The assertion name is the filename.
3 | // Example usage:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // For more information on custom assertions see:
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 |
10 | exports.assertion = function (selector, count) {
11 | this.message = 'Testing if element <' + selector + '> has count: ' + count
12 | this.expected = count
13 | this.pass = function (val) {
14 | return val === this.expected
15 | }
16 | this.value = function (res) {
17 | return res.value
18 | }
19 | this.command = function (cb) {
20 | var self = this
21 | return this.api.execute(function (selector) {
22 | return document.querySelectorAll(selector).length
23 | }, [selector], function (res) {
24 | cb.call(self, res)
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | var config = require('../../config')
3 |
4 | // http://nightwatchjs.org/gettingstarted#settings-file
5 | module.exports = {
6 | src_folders: ['test/e2e/specs'],
7 | output_folder: 'test/e2e/reports',
8 | custom_assertions_path: ['test/e2e/custom-assertions'],
9 |
10 | selenium: {
11 | start_process: true,
12 | server_path: require('selenium-server').path,
13 | host: '127.0.0.1',
14 | port: 4444,
15 | cli_args: {
16 | 'webdriver.chrome.driver': require('chromedriver').path
17 | }
18 | },
19 |
20 | test_settings: {
21 | default: {
22 | selenium_port: 4444,
23 | selenium_host: 'localhost',
24 | silent: true,
25 | globals: {
26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 | }
28 | },
29 |
30 | chrome: {
31 | desiredCapabilities: {
32 | browserName: 'chrome',
33 | javascriptEnabled: true,
34 | acceptSslCerts: true
35 | }
36 | },
37 |
38 | firefox: {
39 | desiredCapabilities: {
40 | browserName: 'firefox',
41 | javascriptEnabled: true,
42 | acceptSslCerts: true
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 |
4 | const webpack = require('webpack')
5 | const DevServer = require('webpack-dev-server')
6 |
7 | const webpackConfig = require('../../build/webpack.prod.conf')
8 | const devConfigPromise = require('../../build/webpack.dev.conf')
9 |
10 | let server
11 |
12 | devConfigPromise.then(devConfig => {
13 | const devServerOptions = devConfig.devServer
14 | const compiler = webpack(webpackConfig)
15 | server = new DevServer(compiler, devServerOptions)
16 | const port = devServerOptions.port
17 | const host = devServerOptions.host
18 | return server.listen(port, host)
19 | })
20 | .then(() => {
21 | // 2. run the nightwatch test suite against it
22 | // to run in additional browsers:
23 | // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings"
24 | // 2. add it to the --env flag below
25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
26 | // For more information on Nightwatch's config file, see
27 | // http://nightwatchjs.org/guide#settings-file
28 | let opts = process.argv.slice(2)
29 | if (opts.indexOf('--config') === -1) {
30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
31 | }
32 | if (opts.indexOf('--env') === -1) {
33 | opts = opts.concat(['--env', 'chrome'])
34 | }
35 |
36 | const spawn = require('cross-spawn')
37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
38 |
39 | runner.on('exit', function (code) {
40 | server.close()
41 | process.exit(code)
42 | })
43 |
44 | runner.on('error', function (err) {
45 | server.close()
46 | throw err
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': function (browser) {
6 | // automatically uses dev Server port from /config.index.js
7 | // default: http://localhost:8080
8 | // see nightwatch.conf.js
9 | const devServer = browser.globals.devServerURL
10 |
11 | browser
12 | .url(devServer)
13 | .waitForElementVisible('#app', 5000)
14 | .end()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/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('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/test/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('../../build/webpack.test.conf')
7 |
8 | module.exports = function karmaConfig (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: ['PhantomJS'],
15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
16 | reporters: ['spec', 'coverage'],
17 | files: ['./index.js'],
18 | preprocessors: {
19 | './index.js': ['webpack', 'sourcemap']
20 | },
21 | webpack: webpackConfig,
22 | webpackMiddleware: {
23 | noInfo: true
24 | },
25 | coverageReporter: {
26 | dir: './coverage',
27 | reporters: [
28 | { type: 'lcov', subdir: '.' },
29 | { type: 'text-summary' }
30 | ]
31 | }
32 | })
33 | }
34 |
--------------------------------------------------------------------------------