[advancedcontentfilter] Add Composer dependencies

- slim/slim: ^3.1
- symfony/expression-language: ^3.4
- fxp/composer-asset-plugin: ~1.3
- bower-asset/vue: ^2.5
- bower-asset/vue-resource: ^1.5
pull/586/head
Hypolite Petovan 2018-04-16 22:11:51 -04:00
parent e181c79e95
commit 20862be7d0
1380 changed files with 190465 additions and 0 deletions

View File

@ -0,0 +1,34 @@
{
"name": "friendica-addons/advancedcontentfilter",
"description": "Advanced Content Filter addon for Friendica",
"type": "friendica-addon",
"authors": [
{
"name": "Hypolite Petovan",
"email": "mrpetovan@gmail.com",
"homepage": "https://friendica.mrpetovan.com/profile/hypolite",
"role": "Developer"
}
],
"require": {
"php": ">=5.5.0",
"slim/slim": "^3.1",
"symfony/expression-language": "^3.4",
"fxp/composer-asset-plugin": "~1.3",
"bower-asset/vue": "^2.5",
"bower-asset/vue-resource": "^1.5"
},
"license": "3-clause BSD license",
"minimum-stability": "stable",
"config": {
"optimize-autoloader": true,
"autoloader-suffix": "AdvancedContentFilterAddon",
"preferred-install": "dist",
"fxp-asset": {
"installer-paths": {
"npm-asset-library": "vendor/asset",
"bower-asset-library": "vendor/asset"
}
}
}
}

745
advancedcontentfilter/composer.lock generated Normal file
View File

@ -0,0 +1,745 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "ac524148018bc5ba29d74e49492be515",
"packages": [
{
"name": "bower-asset/vue",
"version": "v2.5.16",
"source": {
"type": "git",
"url": "https://github.com/vuejs/vue.git",
"reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vuejs/vue/zipball/25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6",
"reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6",
"shasum": ""
},
"type": "bower-asset-library"
},
{
"name": "bower-asset/vue-resource",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/pagekit/vue-resource.git",
"reference": "9a34f881f56f64b923572541d1753cb6cdd63d40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pagekit/vue-resource/zipball/9a34f881f56f64b923572541d1753cb6cdd63d40",
"reference": "9a34f881f56f64b923572541d1753cb6cdd63d40",
"shasum": ""
},
"type": "bower-asset-library",
"extra": {
"bower-asset-main": "dist/vue-resource.js",
"bower-asset-ignore": [
".*",
"build",
"docs",
"package.json"
]
},
"license": [
"MIT"
],
"description": "The HTTP client for Vue.js",
"keywords": [
"ajax",
"http",
"vue",
"xhr"
],
"time": "2018-03-03T07:42:38+00:00"
},
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "fxp/composer-asset-plugin",
"version": "v1.4.2",
"source": {
"type": "git",
"url": "https://github.com/fxpio/composer-asset-plugin.git",
"reference": "61352d99940d2b2392a5d2db83b8c0ef5faf222a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fxpio/composer-asset-plugin/zipball/61352d99940d2b2392a5d2db83b8c0ef5faf222a",
"reference": "61352d99940d2b2392a5d2db83b8c0ef5faf222a",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0",
"php": ">=5.3.3"
},
"require-dev": {
"composer/composer": "^1.4.0"
},
"type": "composer-plugin",
"extra": {
"class": "Fxp\\Composer\\AssetPlugin\\FxpAssetPlugin",
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"Fxp\\Composer\\AssetPlugin\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "François Pluchino",
"email": "francois.pluchino@gmail.com"
}
],
"description": "NPM/Bower Dependency Manager for Composer",
"homepage": "https://github.com/fxpio/composer-asset-plugin",
"keywords": [
"asset",
"bower",
"composer",
"dependency manager",
"nodejs",
"npm",
"package"
],
"time": "2017-10-20T06:53:56+00:00"
},
{
"name": "nikic/fast-route",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|~5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
],
"time": "2018-02-13T20:26:39+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/cache",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"time": "2016-08-06T20:24:11+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "psr/log",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2016-10-10T12:19:37+00:00"
},
{
"name": "psr/simple-cache",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
],
"time": "2017-10-23T01:57:42+00:00"
},
{
"name": "slim/slim",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/container": "^1.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "https://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
],
"time": "2017-11-26T19:13:09+00:00"
},
{
"name": "symfony/cache",
"version": "v3.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "13255ddd056e49f3154747943f8ee175d555d394"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/13255ddd056e49f3154747943f8ee175d555d394",
"reference": "13255ddd056e49f3154747943f8ee175d555d394",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"psr/cache": "~1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"symfony/polyfill-apcu": "~1.1"
},
"conflict": {
"symfony/var-dumper": "<3.3"
},
"provide": {
"psr/cache-implementation": "1.0",
"psr/simple-cache-implementation": "1.0"
},
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/cache": "~1.6",
"doctrine/dbal": "~2.4",
"predis/predis": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Cache\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Cache component with PSR-6, PSR-16, and tags",
"homepage": "https://symfony.com",
"keywords": [
"caching",
"psr6"
],
"time": "2018-04-02T14:35:16+00:00"
},
{
"name": "symfony/expression-language",
"version": "v3.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/expression-language/zipball/867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/cache": "~3.1|~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\ExpressionLanguage\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony ExpressionLanguage Component",
"homepage": "https://symfony.com",
"time": "2018-01-03T07:37:34+00:00"
},
{
"name": "symfony/polyfill-apcu",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-apcu.git",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/e8ae2136ddb53dea314df56fcd88e318ab936c00",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Apcu\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"apcu",
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2018-01-30T19:27:44+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.0"
},
"platform-dev": []
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2017 steffans
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,59 @@
# vue-resource [![Build](https://img.shields.io/circleci/project/pagekit/vue-resource/develop.svg)](https://circleci.com/gh/pagekit/vue-resource) [![Downloads](https://img.shields.io/npm/dm/vue-resource.svg)](https://www.npmjs.com/package/vue-resource) [![jsdelivr](https://data.jsdelivr.com/v1/package/npm/vue-resource/badge?style=rounded)](https://www.jsdelivr.com/package/npm/vue-resource) [![Version](https://img.shields.io/npm/v/vue-resource.svg)](https://www.npmjs.com/package/vue-resource) [![License](https://img.shields.io/npm/l/vue-resource.svg)](https://www.npmjs.com/package/vue-resource)
The plugin for [Vue.js](http://vuejs.org) provides services for making web requests and handle responses using a [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) or JSONP.
## Features
- Supports the [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) API and [URI Templates](https://medialize.github.io/URI.js/uri-template.html)
- Supports [interceptors](docs/http.md#interceptors) for request and response
- Supports latest Firefox, Chrome, Safari, Opera and IE9+
- Supports Vue 1.0 & Vue 2.0
- Compact size 14KB (5.3KB gzipped)
## Installation
You can install it via [yarn](https://yarnpkg.com/) or [NPM](http://npmjs.org/).
```
$ yarn add vue-resource
$ npm install vue-resource
```
### CDN
Available on [jsdelivr](https://cdn.jsdelivr.net/npm/vue-resource@1.5.0), [unpkg](https://unpkg.com/vue-resource@1.5.0) or [cdnjs](https://cdnjs.com/libraries/vue-resource).
```html
<script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.0"></script>
```
## Example
```js
{
// GET /someUrl
this.$http.get('/someUrl').then(response => {
// get body data
this.someData = response.body;
}, response => {
// error callback
});
}
```
## Documentation
- [Configuration](docs/config.md)
- [HTTP Requests/Response](docs/http.md)
- [Creating Resources](docs/resource.md)
- [Code Recipes](docs/recipes.md)
- [API Reference](docs/api.md)
## Changelog
Details changes for each release are documented in the [release notes](https://github.com/pagekit/vue-resource/releases).
## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/pagekit/vue-resource/issues) or a [pull request](https://github.com/pagekit/vue-resource/pulls).
## License
[MIT](http://opensource.org/licenses/MIT)

View File

@ -0,0 +1,20 @@
{
"name": "vue-resource",
"main": "dist/vue-resource.js",
"version": "1.5.0",
"description": "The HTTP client for Vue.js",
"homepage": "https://github.com/pagekit/vue-resource",
"license": "MIT",
"keywords": [
"vue",
"xhr",
"http",
"ajax"
],
"ignore": [
".*",
"build",
"docs",
"package.json"
]
}

View File

@ -0,0 +1,3 @@
# NOTE!
The `dist` folder contains the standalone build for vue-resource, however files here are only checked-in when a release happens. If you are on the `dev` branch, files here are **NOT** up to date. Only the `master` branch contains the built files for the latest stable version.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,65 @@
/**
* Base client.
*/
import Promise from '../../promise';
import xhrClient from './xhr';
import nodeClient from './node';
import {warn, when, isObject, isFunction, inBrowser} from '../../util';
export default function (context) {
const reqHandlers = [sendRequest], resHandlers = [];
if (!isObject(context)) {
context = null;
}
function Client(request) {
while (reqHandlers.length) {
const handler = reqHandlers.pop();
if (isFunction(handler)) {
let response, next;
response = handler.call(context, request, val => next = val) || next;
if (isObject(response)) {
return new Promise((resolve, reject) => {
resHandlers.forEach(handler => {
response = when(response, response => {
return handler.call(context, response) || response;
}, reject);
});
when(response, resolve, reject);
}, context);
}
if (isFunction(response)) {
resHandlers.unshift(response);
}
} else {
warn(`Invalid interceptor of type ${typeof handler}, must be a function`);
}
}
}
Client.use = handler => {
reqHandlers.push(handler);
};
return Client;
}
function sendRequest(request) {
const client = request.client || (inBrowser ? xhrClient : nodeClient);
return client(request);
}

View File

@ -0,0 +1,53 @@
/**
* JSONP client (Browser).
*/
import Promise from '../../promise';
export default function (request) {
return new Promise(resolve => {
var name = request.jsonp || 'callback', callback = request.jsonpCallback || '_jsonp' + Math.random().toString(36).substr(2), body = null, handler, script;
handler = ({type}) => {
var status = 0;
if (type === 'load' && body !== null) {
status = 200;
} else if (type === 'error') {
status = 500;
}
if (status && window[callback]) {
delete window[callback];
document.body.removeChild(script);
}
resolve(request.respondWith(body, {status}));
};
window[callback] = result => {
body = JSON.stringify(result);
};
request.abort = () => {
handler({type: 'abort'});
};
request.params[name] = callback;
if (request.timeout) {
setTimeout(request.abort, request.timeout);
}
script = document.createElement('script');
script.src = request.getUrl();
script.type = 'text/javascript';
script.async = true;
script.onload = handler;
script.onerror = handler;
document.body.appendChild(script);
});
}

View File

@ -0,0 +1,38 @@
/**
* Http client (Node).
*/
import Promise from '../../promise';
import {each, trim} from '../../util';
export default function (request) {
const client = require('got');
return new Promise(resolve => {
var url = request.getUrl();
var body = request.getBody();
var method = request.method;
var headers = {}, handler;
request.headers.forEach((value, name) => {
headers[name] = value;
});
client(url, {body, method, headers}).then(handler = (resp) => {
var response = request.respondWith(resp.body, {
status: resp.statusCode,
statusText: trim(resp.statusMessage)
});
each(resp.headers, (value, name) => {
response.headers.set(name, value);
});
resolve(response);
}, error => handler(error.response));
});
}

View File

@ -0,0 +1,38 @@
/**
* XDomain client (Internet Explorer).
*/
import Promise from '../../promise';
export default function (request) {
return new Promise(resolve => {
var xdr = new XDomainRequest(), handler = ({type}) => {
var status = 0;
if (type === 'load') {
status = 200;
} else if (type === 'error') {
status = 500;
}
resolve(request.respondWith(xdr.responseText, {status}));
};
request.abort = () => xdr.abort();
xdr.open(request.method, request.getUrl());
if (request.timeout) {
xdr.timeout = request.timeout;
}
xdr.onload = handler;
xdr.onabort = handler;
xdr.onerror = handler;
xdr.ontimeout = handler;
xdr.onprogress = () => {};
xdr.send(request.getBody());
});
}

View File

@ -0,0 +1,74 @@
/**
* XMLHttp client (Browser).
*/
import Promise from '../../promise';
import {each, trim, isFunction} from '../../util';
export default function (request) {
return new Promise(resolve => {
var xhr = new XMLHttpRequest(), handler = (event) => {
var response = request.respondWith(
'response' in xhr ? xhr.response : xhr.responseText, {
status: xhr.status === 1223 ? 204 : xhr.status, // IE9 status bug
statusText: xhr.status === 1223 ? 'No Content' : trim(xhr.statusText)
});
each(trim(xhr.getAllResponseHeaders()).split('\n'), row => {
response.headers.append(row.slice(0, row.indexOf(':')), row.slice(row.indexOf(':') + 1));
});
resolve(response);
};
request.abort = () => xhr.abort();
xhr.open(request.method, request.getUrl(), true);
if (request.timeout) {
xhr.timeout = request.timeout;
}
if (request.responseType && 'responseType' in xhr) {
xhr.responseType = request.responseType;
}
if (request.withCredentials || request.credentials) {
xhr.withCredentials = true;
}
if (!request.crossOrigin) {
request.headers.set('X-Requested-With', 'XMLHttpRequest');
}
// deprecated use downloadProgress
if (isFunction(request.progress) && request.method === 'GET') {
xhr.addEventListener('progress', request.progress);
}
if (isFunction(request.downloadProgress)) {
xhr.addEventListener('progress', request.downloadProgress);
}
// deprecated use uploadProgress
if (isFunction(request.progress) && /^(POST|PUT)$/i.test(request.method)) {
xhr.upload.addEventListener('progress', request.progress);
}
if (isFunction(request.uploadProgress) && xhr.upload) {
xhr.upload.addEventListener('progress', request.uploadProgress);
}
request.headers.forEach((value, name) => {
xhr.setRequestHeader(name, value);
});
xhr.onload = handler;
xhr.onabort = handler;
xhr.onerror = handler;
xhr.ontimeout = handler;
xhr.send(request.getBody());
});
}

View File

@ -0,0 +1,75 @@
/**
* HTTP Headers.
*/
import {each, trim, toLower} from '../util';
export default class Headers {
constructor(headers) {
this.map = {};
each(headers, (value, name) => this.append(name, value));
}
has(name) {
return getName(this.map, name) !== null;
}
get(name) {
var list = this.map[getName(this.map, name)];
return list ? list.join() : null;
}
getAll(name) {
return this.map[getName(this.map, name)] || [];
}
set(name, value) {
this.map[normalizeName(getName(this.map, name) || name)] = [trim(value)];
}
append(name, value) {
var list = this.map[getName(this.map, name)];
if (list) {
list.push(trim(value));
} else {
this.set(name, value);
}
}
delete(name) {
delete this.map[getName(this.map, name)];
}
deleteAll() {
this.map = {};
}
forEach(callback, thisArg) {
each(this.map, (list, name) => {
each(list, value => callback.call(thisArg, value, name, this));
});
}
}
function getName(map, name) {
return Object.keys(map).reduce((prev, curr) => {
return toLower(name) === toLower(curr) ? curr : prev;
}, null);
}
function normalizeName(name) {
if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) {
throw new TypeError('Invalid character in header field name');
}
return trim(name);
}

View File

@ -0,0 +1,80 @@
/**
* Service for sending network requests.
*/
const COMMON_HEADERS = {'Accept': 'application/json, text/plain, */*'};
const JSON_CONTENT_TYPE = {'Content-Type': 'application/json;charset=utf-8'};
import cors from './interceptor/cors';
import form from './interceptor/form';
import json from './interceptor/json';
import jsonp from './interceptor/jsonp';
import before from './interceptor/before';
import method from './interceptor/method';
import header from './interceptor/header';
import Client from './client/index';
import Request from './request';
import Promise from '../promise';
import {assign, defaults, error, isString, isFunction} from '../util';
export default function Http(options) {
var self = this || {}, client = Client(self.$vm);
defaults(options || {}, self.$options, Http.options);
Http.interceptors.forEach(handler => {
if (isString(handler)) {
handler = Http.interceptor[handler];
}
if (isFunction(handler)) {
client.use(handler);
}
});
return client(new Request(options)).then(response => {
return response.ok ? response : Promise.reject(response);
}, response => {
if (response instanceof Error) {
error(response);
}
return Promise.reject(response);
});
}
Http.options = {};
Http.headers = {
put: JSON_CONTENT_TYPE,
post: JSON_CONTENT_TYPE,
patch: JSON_CONTENT_TYPE,
delete: JSON_CONTENT_TYPE,
common: COMMON_HEADERS,
custom: {}
};
Http.interceptor = {before, method, jsonp, json, form, header, cors};
Http.interceptors = ['before', 'method', 'jsonp', 'json', 'form', 'header', 'cors'];
['get', 'delete', 'head', 'jsonp'].forEach(method => {
Http[method] = function (url, options) {
return this(assign(options || {}, {url, method}));
};
});
['post', 'put', 'patch'].forEach(method => {
Http[method] = function (url, body, options) {
return this(assign(options || {}, {url, method, body}));
};
});

View File

@ -0,0 +1,13 @@
/**
* Before Interceptor.
*/
import {isFunction} from '../../util';
export default function (request) {
if (isFunction(request.before)) {
request.before.call(this, request);
}
}

View File

@ -0,0 +1,29 @@
/**
* CORS Interceptor.
*/
import Url from '../../url/index';
import xdrClient from '../client/xdr';
import {inBrowser} from '../../util';
const SUPPORTS_CORS = inBrowser && 'withCredentials' in new XMLHttpRequest();
export default function (request) {
if (inBrowser) {
const orgUrl = Url.parse(location.href);
const reqUrl = Url.parse(request.getUrl());
if (reqUrl.protocol !== orgUrl.protocol || reqUrl.host !== orgUrl.host) {
request.crossOrigin = true;
request.emulateHTTP = false;
if (!SUPPORTS_CORS) {
request.client = xdrClient;
}
}
}
}

View File

@ -0,0 +1,17 @@
/**
* Form data Interceptor.
*/
import Url from '../../url/index';
import {isObject, isFormData} from '../../util';
export default function (request) {
if (isFormData(request.body)) {
request.headers.delete('Content-Type');
} else if (isObject(request.body) && request.emulateJSON) {
request.body = Url.params(request.body);
request.headers.set('Content-Type', 'application/x-www-form-urlencoded');
}
}

View File

@ -0,0 +1,21 @@
/**
* Header Interceptor.
*/
import Http from '../index';
import {assign, each, toLower} from '../../util';
export default function (request) {
const headers = assign({}, Http.headers.common,
!request.crossOrigin ? Http.headers.custom : {},
Http.headers[toLower(request.method)]
);
each(headers, (value, name) => {
if (!request.headers.has(name)) {
request.headers.set(name, value);
}
});
}

View File

@ -0,0 +1,46 @@
/**
* JSON Interceptor.
*/
import {when, isObject} from '../../util';
export default function (request) {
const type = request.headers.get('Content-Type') || '';
if (isObject(request.body) && type.indexOf('application/json') === 0) {
request.body = JSON.stringify(request.body);
}
return response => {
return response.bodyText ? when(response.text(), text => {
const type = response.headers.get('Content-Type') || '';
if (type.indexOf('application/json') === 0 || isJson(text)) {
try {
response.body = JSON.parse(text);
} catch (e) {
response.body = null;
}
} else {
response.body = text;
}
return response;
}) : response;
};
}
function isJson(str) {
const start = str.match(/^\s*(\[|\{)/);
const end = {'[': /]\s*$/, '{': /}\s*$/};
return start && end[start[1]].test(str);
}

View File

@ -0,0 +1,13 @@
/**
* JSONP Interceptor.
*/
import jsonpClient from '../client/jsonp';
export default function (request) {
if (request.method == 'JSONP') {
request.client = jsonpClient;
}
}

View File

@ -0,0 +1,12 @@
/**
* HTTP method override Interceptor.
*/
export default function (request) {
if (request.emulateHTTP && /^(PUT|PATCH|DELETE)$/i.test(request.method)) {
request.headers.set('X-HTTP-Method-Override', request.method);
request.method = 'POST';
}
}

View File

@ -0,0 +1,38 @@
/**
* HTTP Request.
*/
import Url from '../url/index';
import Headers from './headers';
import Response from './response';
import {assign, toUpper} from '../util';
export default class Request {
constructor(options) {
this.body = null;
this.params = {};
assign(this, options, {
method: toUpper(options.method || 'GET')
});
if (!(this.headers instanceof Headers)) {
this.headers = new Headers(this.headers);
}
}
getUrl() {
return Url(this);
}
getBody() {
return this.body;
}
respondWith(body, options) {
return new Response(body, assign(options || {}, {url: this.getUrl()}));
}
}

View File

@ -0,0 +1,75 @@
/**
* HTTP Response.
*/
import Headers from './headers';
import Promise from '../promise';
import {when, isBlob, isString} from '../util';
export default class Response {
constructor(body, {url, headers, status, statusText}) {
this.url = url;
this.ok = status >= 200 && status < 300;
this.status = status || 0;
this.statusText = statusText || '';
this.headers = new Headers(headers);
this.body = body;
if (isString(body)) {
this.bodyText = body;
} else if (isBlob(body)) {
this.bodyBlob = body;
if (isBlobText(body)) {
this.bodyText = blobText(body);
}
}
}
blob() {
return when(this.bodyBlob);
}
text() {
return when(this.bodyText);
}
json() {
return when(this.text(), text => JSON.parse(text));
}
}
Object.defineProperty(Response.prototype, 'data', {
get() {
return this.body;
},
set(body) {
this.body = body;
}
});
function blobText(body) {
return new Promise((resolve) => {
var reader = new FileReader();
reader.readAsText(body);
reader.onload = () => {
resolve(reader.result);
};
});
}
function isBlobText(body) {
return body.type.indexOf('text') === 0 || body.type.indexOf('json') !== -1;
}

View File

@ -0,0 +1,57 @@
/**
* Install plugin.
*/
import Url from './url/index';
import Http from './http/index';
import Promise from './promise';
import Resource from './resource';
import Util, {options} from './util';
function plugin(Vue) {
if (plugin.installed) {
return;
}
Util(Vue);
Vue.url = Url;
Vue.http = Http;
Vue.resource = Resource;
Vue.Promise = Promise;
Object.defineProperties(Vue.prototype, {
$url: {
get() {
return options(Vue.url, this, this.$options.url);
}
},
$http: {
get() {
return options(Vue.http, this, this.$options.http);
}
},
$resource: {
get() {
return Vue.resource.bind(this);
}
},
$promise: {
get() {
return (executor) => new Vue.Promise(executor, this);
}
}
});
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(plugin);
}
export default plugin;

View File

@ -0,0 +1,177 @@
/**
* Promises/A+ polyfill v1.1.4 (https://github.com/bramstein/promis)
*/
const RESOLVED = 0;
const REJECTED = 1;
const PENDING = 2;
import {nextTick} from '../util';
export default function Promise(executor) {
this.state = PENDING;
this.value = undefined;
this.deferred = [];
var promise = this;
try {
executor(function (x) {
promise.resolve(x);
}, function (r) {
promise.reject(r);
});
} catch (e) {
promise.reject(e);
}
}
Promise.reject = function (r) {
return new Promise(function (resolve, reject) {
reject(r);
});
};
Promise.resolve = function (x) {
return new Promise(function (resolve, reject) {
resolve(x);
});
};
Promise.all = function all(iterable) {
return new Promise(function (resolve, reject) {
var count = 0, result = [];
if (iterable.length === 0) {
resolve(result);
}
function resolver(i) {
return function (x) {
result[i] = x;
count += 1;
if (count === iterable.length) {
resolve(result);
}
};
}
for (var i = 0; i < iterable.length; i += 1) {
Promise.resolve(iterable[i]).then(resolver(i), reject);
}
});
};
Promise.race = function race(iterable) {
return new Promise(function (resolve, reject) {
for (var i = 0; i < iterable.length; i += 1) {
Promise.resolve(iterable[i]).then(resolve, reject);
}
});
};
var p = Promise.prototype;
p.resolve = function resolve(x) {
var promise = this;
if (promise.state === PENDING) {
if (x === promise) {
throw new TypeError('Promise settled with itself.');
}
var called = false;
try {
var then = x && x['then'];
if (x !== null && typeof x === 'object' && typeof then === 'function') {
then.call(x, function (x) {
if (!called) {
promise.resolve(x);
}
called = true;
}, function (r) {
if (!called) {
promise.reject(r);
}
called = true;
});
return;
}
} catch (e) {
if (!called) {
promise.reject(e);
}
return;
}
promise.state = RESOLVED;
promise.value = x;
promise.notify();
}
};
p.reject = function reject(reason) {
var promise = this;
if (promise.state === PENDING) {
if (reason === promise) {
throw new TypeError('Promise settled with itself.');
}
promise.state = REJECTED;
promise.value = reason;
promise.notify();
}
};
p.notify = function notify() {
var promise = this;
nextTick(function () {
if (promise.state !== PENDING) {
while (promise.deferred.length) {
var deferred = promise.deferred.shift(),
onResolved = deferred[0],
onRejected = deferred[1],
resolve = deferred[2],
reject = deferred[3];
try {
if (promise.state === RESOLVED) {
if (typeof onResolved === 'function') {
resolve(onResolved.call(undefined, promise.value));
} else {
resolve(promise.value);
}
} else if (promise.state === REJECTED) {
if (typeof onRejected === 'function') {
resolve(onRejected.call(undefined, promise.value));
} else {
reject(promise.value);
}
}
} catch (e) {
reject(e);
}
}
}
});
};
p.then = function then(onResolved, onRejected) {
var promise = this;
return new Promise(function (resolve, reject) {
promise.deferred.push([onResolved, onRejected, resolve, reject]);
promise.notify();
});
};
p.catch = function (onRejected) {
return this.then(undefined, onRejected);
};

View File

@ -0,0 +1,150 @@
/**
* URL Template v2.0.6 (https://github.com/bramstein/url-template)
*/
export function expand(url, params, variables) {
var tmpl = parse(url), expanded = tmpl.expand(params);
if (variables) {
variables.push.apply(variables, tmpl.vars);
}
return expanded;
}
export function parse(template) {
var operators = ['+', '#', '.', '/', ';', '?', '&'], variables = [];
return {
vars: variables,
expand(context) {
return template.replace(/\{([^{}]+)\}|([^{}]+)/g, (_, expression, literal) => {
if (expression) {
var operator = null, values = [];
if (operators.indexOf(expression.charAt(0)) !== -1) {
operator = expression.charAt(0);
expression = expression.substr(1);
}
expression.split(/,/g).forEach((variable) => {
var tmp = /([^:*]*)(?::(\d+)|(\*))?/.exec(variable);
values.push.apply(values, getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
variables.push(tmp[1]);
});
if (operator && operator !== '+') {
var separator = ',';
if (operator === '?') {
separator = '&';
} else if (operator !== '#') {
separator = operator;
}
return (values.length !== 0 ? operator : '') + values.join(separator);
} else {
return values.join(',');
}
} else {
return encodeReserved(literal);
}
});
}
};
}
function getValues(context, operator, key, modifier) {
var value = context[key], result = [];
if (isDefined(value) && value !== '') {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
value = value.toString();
if (modifier && modifier !== '*') {
value = value.substring(0, parseInt(modifier, 10));
}
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null));
} else {
if (modifier === '*') {
if (Array.isArray(value)) {
value.filter(isDefined).forEach((value) => {
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null));
});
} else {
Object.keys(value).forEach((k) => {
if (isDefined(value[k])) {
result.push(encodeValue(operator, value[k], k));
}
});
}
} else {
var tmp = [];
if (Array.isArray(value)) {
value.filter(isDefined).forEach((value) => {
tmp.push(encodeValue(operator, value));
});
} else {
Object.keys(value).forEach((k) => {
if (isDefined(value[k])) {
tmp.push(encodeURIComponent(k));
tmp.push(encodeValue(operator, value[k].toString()));
}
});
}
if (isKeyOperator(operator)) {
result.push(encodeURIComponent(key) + '=' + tmp.join(','));
} else if (tmp.length !== 0) {
result.push(tmp.join(','));
}
}
}
} else {
if (operator === ';') {
result.push(encodeURIComponent(key));
} else if (value === '' && (operator === '&' || operator === '?')) {
result.push(encodeURIComponent(key) + '=');
} else if (value === '') {
result.push('');
}
}
return result;
}
function isDefined(value) {
return value !== undefined && value !== null;
}
function isKeyOperator(operator) {
return operator === ';' || operator === '&' || operator === '?';
}
function encodeValue(operator, value, key) {
value = (operator === '+' || operator === '#') ? encodeReserved(value) : encodeURIComponent(value);
if (key) {
return encodeURIComponent(key) + '=' + value;
} else {
return value;
}
}
function encodeReserved(str) {
return str.split(/(%[0-9A-Fa-f]{2})/g).map((part) => {
if (!/%[0-9A-Fa-f]/.test(part)) {
part = encodeURI(part);
}
return part;
}).join('');
}

View File

@ -0,0 +1,77 @@
/**
* Promise adapter.
*/
import PromiseLib from './lib/promise';
if (typeof Promise === 'undefined') {
window.Promise = PromiseLib;
}
export default function PromiseObj(executor, context) {
if (executor instanceof Promise) {
this.promise = executor;
} else {
this.promise = new Promise(executor.bind(context));
}
this.context = context;
}
PromiseObj.all = function (iterable, context) {
return new PromiseObj(Promise.all(iterable), context);
};
PromiseObj.resolve = function (value, context) {
return new PromiseObj(Promise.resolve(value), context);
};
PromiseObj.reject = function (reason, context) {
return new PromiseObj(Promise.reject(reason), context);
};
PromiseObj.race = function (iterable, context) {
return new PromiseObj(Promise.race(iterable), context);
};
var p = PromiseObj.prototype;
p.bind = function (context) {
this.context = context;
return this;
};
p.then = function (fulfilled, rejected) {
if (fulfilled && fulfilled.bind && this.context) {
fulfilled = fulfilled.bind(this.context);
}
if (rejected && rejected.bind && this.context) {
rejected = rejected.bind(this.context);
}
return new PromiseObj(this.promise.then(fulfilled, rejected), this.context);
};
p.catch = function (rejected) {
if (rejected && rejected.bind && this.context) {
rejected = rejected.bind(this.context);
}
return new PromiseObj(this.promise.catch(rejected), this.context);
};
p.finally = function (callback) {
return this.then(function (value) {
callback.call(this);
return value;
}, function (reason) {
callback.call(this);
return Promise.reject(reason);
}
);
};

View File

@ -0,0 +1,76 @@
/**
* Service for interacting with RESTful services.
*/
import Http from './http/index';
import {assign, each, merge} from './util';
export default function Resource(url, params, actions, options) {
var self = this || {}, resource = {};
actions = assign({},
Resource.actions,
actions
);
each(actions, (action, name) => {
action = merge({url, params: assign({}, params)}, options, action);
resource[name] = function () {
return (self.$http || Http)(opts(action, arguments));
};
});
return resource;
}
function opts(action, args) {
var options = assign({}, action), params = {}, body;
switch (args.length) {
case 2:
params = args[0];
body = args[1];
break;
case 1:
if (/^(POST|PUT|PATCH)$/i.test(options.method)) {
body = args[0];
} else {
params = args[0];
}
break;
case 0:
break;
default:
throw 'Expected up to 2 arguments [params, body], got ' + args.length + ' arguments';
}
options.body = body;
options.params = assign({}, options.params, params);
return options;
}
Resource.actions = {
get: {method: 'GET'},
save: {method: 'POST'},
query: {method: 'GET'},
update: {method: 'PUT'},
remove: {method: 'DELETE'},
delete: {method: 'DELETE'}
};

View File

@ -0,0 +1,135 @@
/**
* Service for URL templating.
*/
import root from './root';
import query from './query';
import template from './template';
import {each, merge, isArray, isFunction, isObject, isPlainObject, isString} from '../util';
export default function Url(url, params) {
var self = this || {}, options = url, transform;
if (isString(url)) {
options = {url, params};
}
options = merge({}, Url.options, self.$options, options);
Url.transforms.forEach(handler => {
if (isString(handler)) {
handler = Url.transform[handler];
}
if (isFunction(handler)) {
transform = factory(handler, transform, self.$vm);
}
});
return transform(options);
}
/**
* Url options.
*/
Url.options = {
url: '',
root: null,
params: {}
};
/**
* Url transforms.
*/
Url.transform = {template, query, root};
Url.transforms = ['template', 'query', 'root'];
/**
* Encodes a Url parameter string.
*
* @param {Object} obj
*/
Url.params = function (obj) {
var params = [], escape = encodeURIComponent;
params.add = function (key, value) {
if (isFunction(value)) {
value = value();
}
if (value === null) {
value = '';
}
this.push(escape(key) + '=' + escape(value));
};
serialize(params, obj);
return params.join('&').replace(/%20/g, '+');
};
/**
* Parse a URL and return its components.
*
* @param {String} url
*/
Url.parse = function (url) {
var el = document.createElement('a');
if (document.documentMode) {
el.href = url;
url = el.href;
}
el.href = url;
return {
href: el.href,
protocol: el.protocol ? el.protocol.replace(/:$/, '') : '',
port: el.port,
host: el.host,
hostname: el.hostname,
pathname: el.pathname.charAt(0) === '/' ? el.pathname : '/' + el.pathname,
search: el.search ? el.search.replace(/^\?/, '') : '',
hash: el.hash ? el.hash.replace(/^#/, '') : ''
};
};
function factory(handler, next, vm) {
return options => {
return handler.call(vm, options, next);
};
}
function serialize(params, obj, scope) {
var array = isArray(obj), plain = isPlainObject(obj), hash;
each(obj, (value, key) => {
hash = isObject(value) || isArray(value);
if (scope) {
key = scope + '[' + (plain || hash ? key : '') + ']';
}
if (!scope && array) {
params.add(value.name, value.value);
} else if (hash) {
serialize(params, value, key);
} else {
params.add(key, value);
}
});
}

View File

@ -0,0 +1,25 @@
/**
* Query Parameter Transform.
*/
import Url from './index';
import {each} from '../util';
export default function (options, next) {
var urlParams = Object.keys(Url.options.params), query = {}, url = next(options);
each(options.params, (value, key) => {
if (urlParams.indexOf(key) === -1) {
query[key] = value;
}
});
query = Url.params(query);
if (query) {
url += (url.indexOf('?') == -1 ? '?' : '&') + query;
}
return url;
}

View File

@ -0,0 +1,16 @@
/**
* Root Prefix Transform.
*/
import {isString, trimEnd} from '../util';
export default function (options, next) {
var url = next(options);
if (isString(options.root) && !/^(https?:)?\//.test(url)) {
url = trimEnd(options.root, '/') + '/' + url;
}
return url;
}

View File

@ -0,0 +1,16 @@
/**
* URL Template (RFC 6570) Transform.
*/
import {expand} from '../lib/url-template';
export default function (options) {
var variables = [], url = expand(options.url, options.params, variables);
variables.forEach((key) => {
delete options.params[key];
});
return url;
}

View File

@ -0,0 +1,183 @@
/**
* Utility functions.
*/
import Promise from './promise';
var {hasOwnProperty} = {}, {slice} = [], debug = false, ntick;
export const inBrowser = typeof window !== 'undefined';
export default function ({config, nextTick}) {
ntick = nextTick;
debug = config.debug || !config.silent;
}
export function warn(msg) {
if (typeof console !== 'undefined' && debug) {
console.warn('[VueResource warn]: ' + msg);
}
}
export function error(msg) {
if (typeof console !== 'undefined') {
console.error(msg);
}
}
export function nextTick(cb, ctx) {
return ntick(cb, ctx);
}
export function trim(str) {
return str ? str.replace(/^\s*|\s*$/g, '') : '';
}
export function trimEnd(str, chars) {
if (str && chars === undefined) {
return str.replace(/\s+$/, '');
}
if (!str || !chars) {
return str;
}
return str.replace(new RegExp(`[${chars}]+$`), '');
}
export function toLower(str) {
return str ? str.toLowerCase() : '';
}
export function toUpper(str) {
return str ? str.toUpperCase() : '';
}
export const isArray = Array.isArray;
export function isString(val) {
return typeof val === 'string';
}
export function isBoolean(val) {
return val === true || val === false;
}
export function isFunction(val) {
return typeof val === 'function';
}
export function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
export function isPlainObject(obj) {
return isObject(obj) && Object.getPrototypeOf(obj) == Object.prototype;
}
export function isBlob(obj) {
return typeof Blob !== 'undefined' && obj instanceof Blob;
}
export function isFormData(obj) {
return typeof FormData !== 'undefined' && obj instanceof FormData;
}
export function when(value, fulfilled, rejected) {
var promise = Promise.resolve(value);
if (arguments.length < 2) {
return promise;
}
return promise.then(fulfilled, rejected);
}
export function options(fn, obj, opts) {
opts = opts || {};
if (isFunction(opts)) {
opts = opts.call(obj);
}
return merge(fn.bind({$vm: obj, $options: opts}), fn, {$options: opts});
}
export function each(obj, iterator) {
var i, key;
if (isArray(obj)) {
for (i = 0; i < obj.length; i++) {
iterator.call(obj[i], obj[i], i);
}
} else if (isObject(obj)) {
for (key in obj) {
if (hasOwnProperty.call(obj, key)) {
iterator.call(obj[key], obj[key], key);
}
}
}
return obj;
}
export const assign = Object.assign || _assign;
export function merge(target) {
var args = slice.call(arguments, 1);
args.forEach(source => {
_merge(target, source, true);
});
return target;
}
export function defaults(target) {
var args = slice.call(arguments, 1);
args.forEach(source => {
for (var key in source) {
if (target[key] === undefined) {
target[key] = source[key];
}
}
});
return target;
}
function _assign(target) {
var args = slice.call(arguments, 1);
args.forEach(source => {
_merge(target, source);
});
return target;
}
function _merge(target, source, deep) {
for (var key in source) {
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
target[key] = {};
}
if (isArray(source[key]) && !isArray(target[key])) {
target[key] = [];
}
_merge(target[key], source[key], deep);
} else if (source[key] !== undefined) {
target[key] = source[key];
}
}
}

View File

@ -0,0 +1 @@
invalid

View File

@ -0,0 +1 @@
text

View File

@ -0,0 +1 @@
{"foo": "bar"}

View File

@ -0,0 +1,200 @@
import Vue from 'vue';
describe('Vue.http', function () {
it('get("data/text.txt")', done => {
Vue.http.get('data/text.txt').then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(res.body).toBe('text');
expect(res.data).toBe(res.body);
expect(res.headers.get('Content-Type')).toBe('text/plain');
done();
});
});
it('get("data/valid.json")', done => {
Vue.http.get('data/valid.json').then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('object');
expect(res.body.foo).toBe('bar');
done();
});
});
it('get("data/invalid.json")', done => {
Vue.http.get('data/invalid.json').then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(res.body).toBeNull();
done();
});
});
it('get("github.com/avatar")', done => {
Vue.http.get('https://avatars1.githubusercontent.com/u/6128107', {responseType: 'blob'}).then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(res.body instanceof Blob).toBe(true);
expect(res.body.type).toBe('image/png');
done();
});
});
it('get("cors-api.appspot.com")', done => {
Vue.http.get('http://server.cors-api.appspot.com/server?id=1&enable=true').then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('object');
expect(res.body.shift().requestType).toBe('cors');
if (res.headers.get('Content-Type')) {
expect(res.headers.get('Content-Type')).toBe('application/json');
}
done();
});
});
it('jsonp("jsfiddle.net/jsonp")', done => {
Vue.http.jsonp('http://jsfiddle.net/echo/jsonp/', {params: {foo: 'bar'}}).then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('object');
expect(res.body.foo).toBe('bar');
done();
});
});
});
describe('this.$http', function () {
it('get("data/valid.json")', done => {
var vm = new Vue({
created() {
this.$http.get('data/valid.json').then(res => {
expect(this).toBe(vm);
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('object');
expect(res.body.foo).toBe('bar');
done();
});
}
});
});
it('get("data/valid.json") with timeout', done => {
var vm = new Vue({
created() {
var random = Math.random().toString(36).substr(2);
this.$http.get(`data/valid.json?${random}`, {timeout: 1}).then(res => {
fail('Callback has been called');
}, res => {
expect(res.ok).toBe(false);
expect(res.status).toBe(0);
done();
});
}
});
});
it('get("data/valid.json") with abort()', done => {
var vm = new Vue({
created() {
var random = Math.random().toString(36).substr(2);
this.$http.get(`data/valid.json?${random}`, {
before(req) {
setTimeout(() => {
expect(typeof req.abort).toBe('function');
req.abort();
}, 0);
}
}).then(res => {
fail('Callback has been called');
}, res => {
done();
});
}
});
});
it('get("data/notfound.json") using catch()', done => {
var vm = new Vue({
created() {
this.$http.get('data/notfound.json').catch(res => {
expect(this).toBe(vm);
expect(res.ok).toBe(false);
expect(res.status).toBe(404);
done();
});
}
});
});
});

View File

@ -0,0 +1,34 @@
var Vue = require('vue');
var VueResource = require('../dist/vue-resource.common.js');
Vue.use(VueResource);
describe('Vue.http', function () {
it('post("jsfiddle.net/html")', () => {
return Vue.http.post('http://jsfiddle.net/echo/html/', {html: 'text'}, {emulateJSON: true}).then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('string');
expect(res.body).toBe('text');
});
});
it('post("jsfiddle.net/json")', () => {
return Vue.http.post('http://jsfiddle.net/echo/json/', {json: JSON.stringify({foo: 'bar'})}, {emulateJSON: true}).then(res => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(typeof res.body).toBe('object');
expect(res.body.foo).toBe('bar');
});
});
});

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue Resource - Jasmine</title>
<link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png">
<link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<script src="specs.js"></script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,9 @@
import Vue from 'vue';
import VueResource from '../src/index';
Vue.use(VueResource);
require('./url');
require('./http');
require('./resource');
require('./promise');

View File

@ -0,0 +1,26 @@
module.exports = config => {
config.set({
basePath: __dirname,
frameworks: ['jasmine'],
browsers: ['Chrome', 'Safari', 'Firefox'],
files: [
'index.js',
{pattern: 'data/*', included: false},
],
preprocessors: {
'index.js': ['webpack']
},
proxies: {
'/data/': '/base/data/'
},
});
};

View File

@ -0,0 +1,125 @@
import Promise from '../src/promise';
describe('Vue.promise ' + (window.Promise !== undefined ? '(native)' : '(polyfill)'), function () {
it('then fulfill', function (done) {
Promise.resolve(1).then(function (value) {
expect(value).toBe(1);
done();
});
});
it('then reject', function (done) {
Promise.reject(1).then(undefined, function (value) {
expect(value).toBe(1);
done();
});
});
it('catch', function (done) {
Promise.reject(1).catch(function (value) {
expect(value).toBe(1);
done();
});
});
it('finally fulfill', function (done) {
Promise.resolve(1).finally(function (arg) {
expect(arg).toBe(undefined);
}).then(function (arg) {
expect(arg).toBe(1);
done();
});
});
it('finally reject', function (done) {
Promise.reject(1).finally(function (arg) {
expect(arg).toBe(undefined);
}).catch(function (arg) {
expect(arg).toBe(1);
done();
});
});
it('all', function (done) {
Promise.all([
Promise.resolve(1),
Promise.resolve(2)
]).then(function (values) {
expect(values[0]).toBe(1);
expect(values[1]).toBe(2);
done();
});
});
it('duplicate', function (done) {
Promise.all([
Promise.resolve(1).then(function (value) {
expect(value).toBe(1);
}),
Promise.resolve(2).then(function (value) {
expect(value).toBe(2);
})
]).then(done);
});
it('context', function (done) {
var context = {foo: 'bar'};
Promise.resolve().bind(context).then(function () {
expect(this).toBe(context);
done();
});
});
it('context chain fulfill', function (done) {
var context = {foo: 'bar'};
Promise.resolve().bind(context).catch(undefined).finally(function () {
expect(this).toBe(context);
}).then(function () {
expect(this).toBe(context);
done();
});
});
it('context chain reject', function (done) {
var context = {foo: 'bar'};
Promise.reject().bind(context).catch(function () {
expect(this).toBe(context);
return Promise.reject();
}).finally(function () {
expect(this).toBe(context);
}).catch(function () {
expect(this).toBe(context);
done();
});
});
});

View File

@ -0,0 +1,51 @@
import Vue from 'vue';
describe('this.$resource', function () {
it('get({file: "valid.json"})', (done) => {
var vm = new Vue({
created() {
var resource = this.$resource('data{/file}');
resource.get({file: 'valid.json'}).then((res) => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(res.data.foo).toBe('bar');
done();
});
}
});
});
it('save({file: "valid.json"}, {foo: "bar"})', (done) => {
var vm = new Vue({
created() {
var resource = this.$resource('data{/file}');
resource.save({file: 'valid.json'}, {foo: 'bar'}).then((res) => {
expect(res.ok).toBe(true);
expect(res.status).toBe(200);
expect(res.data.foo).toBe('bar');
done();
});
}
});
});
});

View File

@ -0,0 +1,33 @@
import Vue from 'vue';
describe('Vue.url', function () {
it('data{/id}', function () {
expect(Vue.url('data{/id}')).toBe('data');
expect(Vue.url('data{/id}', {id: 1})).toBe('data/1');
});
it('data{/array}', function () {
expect(Vue.url('data{?array}')).toBe('data');
expect(Vue.url('data{?array}', {array: [1,2,3]})).toBe('data?array=1,2,3');
});
it('{+path}data', function () {
expect(Vue.url('{+path}data')).toBe('data');
expect(Vue.url('{+path}data', {path: 'path1/path2/'})).toBe('path1/path2/data');
});
it('{+base}data', function () {
Vue.url.options.params.base = 'base/path/';
expect(Vue.url('{+base}data')).toBe('base/path/data');
});
});

View File

@ -0,0 +1,17 @@
var webpack = require('webpack');
module.exports = {
entry: __dirname + '/index.js',
output: {
path: __dirname + '/',
filename: 'specs.js'
},
module: {
loaders: [
{test: /\.js$/, loader: 'buble-loader', exclude: /node_modules/}
]
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};

View File

@ -0,0 +1,94 @@
import _Vue = require('vue');
// augment typings of Vue.js
import './vue';
export interface HttpHeaders {
put?: { [key: string]: string };
post?: { [key: string]: string };
patch?: { [key: string]: string };
delete?: { [key: string]: string };
common?: { [key: string]: string };
custom?: { [key: string]: string };
[key: string]: any;
}
export interface HttpResponse {
data: any;
ok: boolean;
status: number;
statusText: string;
headers: Function;
text():string;
json():any;
blob():Blob;
}
export interface HttpOptions {
url?: string;
method?: string;
body?: any;
params?: any;
headers?: any;
before?(request: any): any;
progress?(event: any): any;
credentials?:boolean;
emulateHTTP?: boolean;
emulateJSON?: boolean;
}
export interface $http {
(url: string, data?: any, options?: HttpOptions): PromiseLike<HttpResponse>;
(url: string, options?: HttpOptions): PromiseLike<HttpResponse>;
}
export interface HttpInterceptor {
request?(request: HttpOptions): HttpOptions;
response?(response: HttpResponse): HttpResponse;
}
export interface Http {
options: HttpOptions & { root: string };
headers: HttpHeaders;
interceptors: (HttpInterceptor | (() => HttpInterceptor))[];
get: $http;
post: $http;
put: $http;
patch: $http;
delete: $http;
jsonp: $http;
}
export interface ResourceActions {
get: { method: string };
save: { method: string };
query: { method: string };
update: { method: string };
remove: { method: string };
delete: { method: string };
}
export interface ResourceMethod {
(params: any, data?: any, success?: Function, error?: Function): PromiseLike<HttpResponse>;
(params: any, success?: Function, error?: Function): PromiseLike<HttpResponse>;
(success?: Function, error?: Function): PromiseLike<HttpResponse>;
}
export interface ResourceMethods {
get: ResourceMethod;
save: ResourceMethod;
query: ResourceMethod;
update: ResourceMethod;
remove: ResourceMethod;
delete: ResourceMethod;
}
export interface $resource {
(url: string, params?: any, actions?: any, options?: HttpOptions): ResourceMethods;
}
export interface Resource extends $resource {
actions: ResourceActions;
}
export declare function install(vue: typeof _Vue): void;

View File

@ -0,0 +1,27 @@
/**
* Extends interfaces in Vue.js
*/
import Vue from "vue";
import { HttpHeaders, HttpOptions, HttpResponse, $http, $resource } from "./index";
declare module "vue/types/options" {
interface ComponentOptions<V extends Vue> {
http?: (HttpOptions & { headers?: HttpHeaders } & { [key: string]: any })
}
}
declare module "vue/types/vue" {
interface Vue {
$http: {
(options: HttpOptions): PromiseLike<HttpResponse>;
get: $http;
post: $http;
put: $http;
patch: $http;
delete: $http;
jsonp: $http;
};
$resource: $resource;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
{
"presets": ["es2015", "flow-vue"],
"plugins": ["transform-vue-jsx", "syntax-dynamic-import"],
"ignore": [
"dist/*.js",
"packages/**/*.js"
]
}

View File

@ -0,0 +1,81 @@
version: 2
defaults: &defaults
working_directory: ~/project/vue
docker:
- image: circleci/node:6-browsers
jobs:
install:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }}
- v1-vue-{{ .Branch }}-
- v1-vue-
- run: npm install
- save_cache:
key: v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }}
paths:
- node_modules/
- persist_to_workspace:
root: ~/project
paths:
- vue
lint-flow-types:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: npm run lint
- run: npm run flow
- run: npm run test:types
test-cover:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: npm run test:cover
- run:
name: report coverage stats for non-PRs
command: |
if [[ -z $CI_PULL_REQUEST ]]; then
./node_modules/.bin/codecov
fi
test-e2e:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: npm run test:e2e -- --env phantomjs
test-ssr-weex:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: npm run test:ssr
- run: npm run test:weex
workflows:
version: 2
install-and-parallel-test:
jobs:
- install
- test-cover:
requires:
- install
- lint-flow-types:
requires:
- install
- test-e2e:
requires:
- install
- test-ssr-weex:
requires:
- install

View File

@ -0,0 +1,15 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

View File

@ -0,0 +1,3 @@
flow
dist
packages

View File

@ -0,0 +1,14 @@
{
"root": true,
"plugins": [
"flowtype"
],
"extends": [
"plugin:vue-libs/recommended",
"plugin:flowtype/recommended"
],
"globals": {
"__WEEX__": true,
"WXEnvironment": true
}
}

View File

@ -0,0 +1,23 @@
[ignore]
.*/node_modules/.*
.*/test/.*
.*/scripts/.*
.*/examples/.*
.*/benchmarks/.*
[include]
[libs]
flow
[options]
unsafe.enable_getters_and_setters=true
module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1'
module.name_mapper='^core/\(.*\)$' -> '<PROJECT_ROOT>/src/core/\1'
module.name_mapper='^shared/\(.*\)$' -> '<PROJECT_ROOT>/src/shared/\1'
module.name_mapper='^web/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/web/\1'
module.name_mapper='^weex/\(.*\)$' -> '<PROJECT_ROOT>/src/platforms/weex/\1'
module.name_mapper='^server/\(.*\)$' -> '<PROJECT_ROOT>/src/server/\1'
module.name_mapper='^entries/\(.*\)$' -> '<PROJECT_ROOT>/src/entries/\1'
module.name_mapper='^sfc/\(.*\)$' -> '<PROJECT_ROOT>/src/sfc/\1'
suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line

View File

@ -0,0 +1,13 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)

View File

@ -0,0 +1,91 @@
## Git Commit Message Convention
> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
#### TL;DR:
Messages must be matched by the following regex:
``` js
/^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types)(\(.+\))?: .{1,50}/
```
#### Examples
Appears under "Features" header, `compiler` subheader:
```
feat(compiler): add 'comments' option
```
Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
```
fix(v-model): handle events on blur
close #28
```
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
```
perf(core): improve vdom diffing by removing 'foo' option
BREAKING CHANGE: The 'foo' option has been removed.
```
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
```
revert: feat(compiler): add 'comments' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
```
### Full Message Format
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
### Scope
The scope could be anything specifying place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...
### Subject
The subject contains succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.

View File

@ -0,0 +1,140 @@
# Vue.js Contributing Guide
Hi! Im really excited that you are interested in contributing to Vue.js. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
- [Code of Conduct](https://github.com/vuejs/vue/blob/dev/.github/CODE_OF_CONDUCT.md)
- [Issue Reporting Guidelines](#issue-reporting-guidelines)
- [Pull Request Guidelines](#pull-request-guidelines)
- [Development Setup](#development-setup)
- [Project Structure](#project-structure)
## Issue Reporting Guidelines
- Always use [https://new-issue.vuejs.org/](https://new-issue.vuejs.org/) to create new issues.
## Pull Request Guidelines
- The `master` branch is basically just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.**
- Checkout a topic branch from the relevant branch, e.g. `dev`, and merge back against that branch.
- Work in the `src` folder and **DO NOT** checkin `dist` in the commits.
- It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging.
- Make sure `npm test` passes. (see [development setup](#development-setup))
- If adding new feature:
- Add accompanying test case.
- Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it.
- If fixing a bug:
- If you are resolving a special issue, add `(fix #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`.
- Provide detailed description of the bug in the PR. Live demo preferred.
- Add appropriate test coverage if applicable.
## Development Setup
You will need [Node.js](http://nodejs.org) **version 6+** and [Java Runtime Environment](http://www.oracle.com/technetwork/java/javase/downloads/index.html) (needed for running Selenium server during e2e tests).
After cloning the repo, run:
``` bash
$ npm install # or yarn
```
### Committing Changes
Commit messages should follow the [commit message convention](./COMMIT_CONVENTION.md) so that changelogs can be automatically generated. Commit messages will be automatically validated upon commit. If you are not familiar with the commit message convention, you can use `npm run commit` instead of `git commit`, which provides an interactive CLI for generating proper commit messages.
### Commonly used NPM scripts
``` bash
# watch and auto re-build dist/vue.js
$ npm run dev
# watch and auto re-run unit tests in Chrome
$ npm run dev:test
# build all dist files, including npm packages
$ npm run build
# run the full test suite, include linting / type checking
$ npm test
```
There are some other scripts available in the `scripts` section of the `package.json` file.
The default test script will do the following: lint with ESLint -> type check with Flow -> unit tests with coverage -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.** Although the same tests will be run against your PR on the CI server, it is better to have it working locally beforehand.
## Project Structure
- **`scripts`**: contains build-related scripts and configuration files. In most cases you don't need to touch them. However, it would be helpful to familiarize yourself with the following files:
- `scripts/alias.js`: module import aliases used across all source code and tests.
- `scripts/config.js`: contains the build configurations for all files found in `dist/`. Check this file if you want to find out the entry source file for a dist file.
- **`dist`**: contains built files for distribution. Note this directory is only updated when a release happens; they do not reflect the latest changes in development branches.
See [dist/README.md](https://github.com/vuejs/vue/blob/dev/dist/README.md) for more details on dist files.
- **`flow`**: contains type declarations for [Flow](https://flowtype.org/). These declarations are loaded **globally** and you will see them used in type annotations in normal source code.
- **`packages`**: contains `vue-server-renderer` and `vue-template-compiler`, which are distributed as separate NPM packages. They are automatically generated from the source code and always have the same version with the main `vue` package.
- **`test`**: contains all tests. The unit tests are written with [Jasmine](http://jasmine.github.io/2.3/introduction.html) and run with [Karma](http://karma-runner.github.io/0.13/index.html). The e2e tests are written for and run with [Nightwatch.js](http://nightwatchjs.org/).
- **`src`**: contains the source code, obviously. The codebase is written in ES2015 with [Flow](https://flowtype.org/) type annotations.
- **`compiler`**: contains code for the template-to-render-function compiler.
The compiler consists of a parser (converts template strings to element ASTs), an optimizer (detects static trees for vdom render optimization), and a code generator (generate render function code from element ASTs). Note the codegen directly generates code strings from the element AST - it's done this way for smaller code size because the compiler is shipped to the browser in the standalone build.
- **`core`**: contains universal, platform-agnostic runtime code.
The Vue 2.0 core is platform-agnostic - which means code inside `core` should be able to run in any JavaScript environment, be it the browser, Node.js, or an embedded JavaScript runtime in native applications.
- **`observer`**: contains code related to the reactivity system.
- **`vdom`**: contains code related to vdom element creation and patching.
- **`instance`**: contains Vue instance constructor and prototype methods.
- **`global-api`**: as the name suggests.
- **`components`**: universal abstract components. Currently `keep-alive` is the only one.
- **`server`**: contains code related to server-side rendering.
- **`platforms`**: contains platform-specific code.
Entry files for dist builds are located in their respective platform directory.
Each platform module contains three parts: `compiler`, `runtime` and `server`, corresponding to the three directories above. Each part contains platform-specific modules/utilities which are then imported and injected to the core counterparts in platform-specific entry files. For example, the code implementing the logic behind `v-bind:class` is in `platforms/web/runtime/modules/class.js` - which is imported in `entries/web-runtime.js` and used to create the browser-specific vdom patching function.
- **`sfc`**: contains single-file component (`*.vue` files) parsing logic. This is used in the `vue-template-compiler` package.
- **`shared`**: contains utilities shared across the entire codebase.
- **`types`**: contains TypeScript type definitions
- **`test`**: type definitions tests
## Financial Contribution
As a pure community-driven project without major corporate backing, we also welcome financial contributions via Patreon or OpenCollective.
- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou)
- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs)
### What's the difference between Patreon and OpenCollective?
Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
## Credits
Thank you to all the people who have already contributed to Vue.js!
<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>

View File

@ -0,0 +1,16 @@
<!--
IMPORTANT: Please use the following link to create a new issue:
https://new-issue.vuejs.org/
If your issue was not created using the app above, it will be closed immediately.
中文用户请注意:
请使用上面的链接来创建新的 issue。如果不是用上述工具创建的 issue 会被自动关闭。
-->
<!--
Love vuejs? Please consider supporting us via Patreon or OpenCollective:
👉 https://www.patreon.com/evanyou
👉 https://opencollective.com/vuejs/donate
-->

View File

@ -0,0 +1,35 @@
<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->
<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->
**What kind of change does this PR introduce?** (check at least one)
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:
**Does this PR introduce a breaking change?** (check one)
- [ ] Yes
- [ ] No
If yes, please describe the impact and migration path for existing applications:
**The PR fulfills these requirements:**
- [ ] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [ ] New/updated tests are included
If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)
**Other information:**

View File

@ -0,0 +1,19 @@
.DS_Store
node_modules
*.log
explorations
TODOs.md
dist/*.gz
dist/*.map
dist/vue.common.min.js
test/e2e/reports
test/e2e/screenshots
coverage
RELEASE_NOTE*.md
dist/*.js
packages/vue-server-renderer/basic.js
packages/vue-server-renderer/build.js
packages/vue-server-renderer/server-plugin.js
packages/vue-server-renderer/client-plugin.js
packages/vue-template-compiler/build.js
.vscode

View File

@ -0,0 +1,348 @@
<h1 align="center">Sponsors &amp; Backers</h1>
Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider:
- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou).
- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs).
- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations)
#### What's the difference between Patreon and OpenCollective?
Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
<br><br>
<h2 align="center">Special Sponsors</h2>
<!--special start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://stdlib.com" target="_blank">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/stdlib.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue" target="_blank">
<img height="90px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bit-wide.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://xiaozhuanlan.com" target="_blank">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/xiaozhuanlan.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--special end-->
<h2 align="center">Platinum via Patreon</h2>
<!--platinum start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="http://tooltwist.com/" target="_blank">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tooltwist.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://vueschool.io/?utm_source=Vuejs.org&utm_medium=Banner&utm_campaign=Sponsored%20Banner&utm_content=V1" target="_blank">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vueschool.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.datacamp.com/careers?utm_source=vuejs&utm_medium=sidebar" target="_blank">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/datacamp.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--platinum end-->
<h2 align="center">Platinum via OpenCollective</h2>
<a href="https://opencollective.com/vuejs/tiers/platinum-sponsors/0/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/platinum-sponsors/0/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/platinum-sponsors/1/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/platinum-sponsors/1/avatar.svg"></a>
<h2 align="center">Gold via Patreon</h2>
<!--gold start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://laravel.com" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://htmlburger.com" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://chaitin.cn/en/" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://anymod.com" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/anymod.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.frontenddeveloperlove.com/" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/frontend-love.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://onsen.io/vue/" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/onsen-ui.png">
</a>
</td>
</tr><tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://vuetifyjs.com" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuetify.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://neds.com.au/" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/neds.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://hackr.io/tutorials/learn-vue-js" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/hackr-io.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://icons8.com/" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/icons8.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://vuejobs.com/?ref=vuejs" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejobs.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://leanpub.com/vuejs2" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tmvuejs2.png">
</a>
</td>
</tr><tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.bmqb.com/jobs" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bmqb.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://codepilot.ai" target="_blank">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/codepilot.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--gold end-->
<h2 align="center">Gold via OpenCollective</h2>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/0/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/0/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/1/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/1/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/2/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/2/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/3/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/3/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/4/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/4/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/5/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/5/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/6/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/6/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/7/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/7/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/8/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/8/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/9/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/9/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/10/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/10/avatar.svg" height="60px"></a>
<h2 align="center">Silver via Patreon</h2>
- Matt Mullenweg
<h2 align="center">Bronze via Patreon</h2>
<!--bronze start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="http://tighten.co/" target="_blank">
<img width="148px" src="http://i.imgur.com/T7fQYLT.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://alligator.io" target="_blank">
<img width="148px" src="https://alligator.io/images/alligator-logo.svg">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.accelebrate.com/" target="_blank">
<img width="148px" src="https://www.accelebrate.com/assets/images/accelebrate_logo@2x.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://pullstring.com" target="_blank">
<img width="148px" src="https://i.imgur.com/hQHW6TB.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--bronze end-->
<h2 align="center">Bronze via OpenCollective</h2>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/0/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/0/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/1/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/1/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/2/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/2/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/3/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/3/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/4/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/4/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/5/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/5/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/6/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/6/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/7/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/7/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/8/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/8/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/9/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/9/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/10/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/10/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/11/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/11/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/12/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/12/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/13/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/13/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/14/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/14/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/15/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/15/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/16/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/16/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/17/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/17/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/18/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/18/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/19/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/19/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/20/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/20/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/21/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/21/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/22/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/22/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/23/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/23/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/24/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/24/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/25/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/25/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/26/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/26/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/27/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/27/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/28/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/28/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/bronze-sponsors/29/website" target="_blank"><img src="https://opencollective.com/vuejs/tiers/bronze-sponsors/29/avatar.svg"></a>
<h2 align="center">Generous Backers via Patreon ($50+)</h2>
<!--50 start-->
- Wasim Khamlichi
- errorrik
- Alex Balashov
- Konstantin Levinski
- Samuel Smith
- HARRI J SALOMAA
- tjkoury
- Evan Leonardi
<!--50 end-->
<h2 align="center">Backers via Patreon</h2>
<!--10 start-->
- Luca Borghini
- kazuya kawaguchi
- Keisuke KITA
- Jack Barham
- Santa Cruz
- Tom Conlon
- Simon East
- Henry Zhu
- Benjamin Listwon
- Lars Andreas Ness
- Victor Tolbert
- Frank Dugan III
- Stephen Hartley
- Wen-Tien Chang
- Kirk Lewis
- Karol F
- Miljan Aleksic
- 叶解
- Paul Straw
- Jake Ingman
- Barbara Liau
- Isaac Sant
- Milos Stojanovic
- 4
- The Graphic Design School
- Christian Griffith
- Jarek Tkaczyk
- Andrew Davis
- Sean MacIsaac
- Xiaojie LI
- David Hess
- Niannian Modisette
- Dariusz Jastrzębski
- Matt Jones
- Dave Chenell
- Cheng-Wei Chien
- Duncan J Kenzie
- Mike Margerum
- Guy Gavergun
- Edithvale
- Intevation GmbH
- Luiz Eduardo Tanure Bacelar
- Chengzhi Yin
- Zoran Knezevic
- James Simpson
- Pierre Vanhulst
- Vincent Gabriel
- Chris Anderson
- Jon Hobbs-Smith
- Akiho Nagao
- Asaf Yishai
- Estebe Anthony
- Haim Yulzari
- David McGuigan
- Jeremy Tan
- Jim Raden
- Fille Åström
- Nicholas Reid
- Tyler Scott
- Thong Yong Jun
- Bryan Gruneberg
- Roman Kuba
- Familiar Studio
- Matias Verdier
- Jamie McElwain
- Vivekanandhan Natarajan
- Rafael Belvederese
- Mickaël Andrieu
- Guilherme S L de Souza
- Rob Yedlin
- Daniel Waghorn
- Chih-Hsuan, Fan
- Jordan Oroshiba
- Brian Jorden
- Cliff Hess
- Joe Ray Gregory
- Johnny Eshan
- Orney Enrique martinez Said
- Tom Striker
- Oskar Lindgren
- RADD Creative
- Maegan Wilson
- Rua Cura D'ars
- Richard Simpson
- Charles McKeever
- Alok Pant
- Jessie Hernandez
- Eric Fong
- Aparajita Fishman
<!--10 end-->
<h2 align="center">Backers via OpenCollective</h2>
<a href="https://opencollective.com/vuejs#backers" target="_blank"><img src="https://opencollective.com/vuejs/backers.svg?width=890"></a>

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,264 @@
<p align="center"><a href="https://vuejs.org" target="_blank" rel="noopener noreferrer"><img width="100" src="https://vuejs.org/images/logo.png" alt="Vue logo"></a></p>
<p align="center">
<a href="https://circleci.com/gh/vuejs/vue/tree/dev"><img src="https://img.shields.io/circleci/project/vuejs/vue/dev.svg" alt="Build Status"></a>
<a href="https://codecov.io/github/vuejs/vue?branch=dev"><img src="https://img.shields.io/codecov/c/github/vuejs/vue/dev.svg" alt="Coverage Status"></a>
<a href="https://npmcharts.com/compare/vue?minimal=true"><img src="https://img.shields.io/npm/dm/vue.svg" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/v/vue.svg" alt="Version"></a>
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/l/vue.svg" alt="License"></a>
<a href="https://chat.vuejs.org/"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat"></a>
<br>
<a href="https://saucelabs.com/u/vuejs"><img src="https://saucelabs.com/browser-matrix/vuejs.svg" alt="Sauce Test Status"></a>
</p>
<h2 align="center">Supporting Vue.js</h2>
Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider:
- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou).
- [Become a backer or sponsor on Open Collective](https://opencollective.com/vuejs).
- [One-time donation via PayPal or crypto-currencies.](https://vuejs.org/support-vuejs/#One-time-Donations)
#### What's the difference between Patreon and OpenCollective?
Funds donated via Patreon go directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
<h3 align="center">Special Sponsors</h3>
<!--special start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://stdlib.com" target="_blank" rel="noopener noreferrer">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/stdlib.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue" target="_blank" rel="noopener noreferrer">
<img height="90px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bit-wide.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://xiaozhuanlan.com" target="_blank" rel="noopener noreferrer">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/xiaozhuanlan.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--special end-->
<h3 align="center">Sponsors via Patreon</h3>
<h4 align="center">Platinum</h4>
<!--platinum start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="http://tooltwist.com/" target="_blank" rel="noopener noreferrer">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tooltwist.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://vueschool.io/?utm_source=Vuejs.org&utm_medium=Banner&utm_campaign=Sponsored%20Banner&utm_content=V1" target="_blank" rel="noopener noreferrer">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vueschool.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.datacamp.com/careers?utm_source=vuejs&utm_medium=sidebar" target="_blank" rel="noopener noreferrer">
<img width="222px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/datacamp.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--platinum end-->
<h4 align="center">Gold</h4>
<!--gold start-->
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://laravel.com" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://htmlburger.com" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://chaitin.cn/en/" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://anymod.com" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/anymod.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.frontenddeveloperlove.com/" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/frontend-love.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://onsen.io/vue/" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/onsen-ui.png">
</a>
</td>
</tr><tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://vuetifyjs.com" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuetify.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://neds.com.au/" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/neds.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://hackr.io/tutorials/learn-vue-js" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/hackr-io.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://icons8.com/" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/icons8.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://vuejobs.com/?ref=vuejs" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/vuejobs.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://leanpub.com/vuejs2" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tmvuejs2.png">
</a>
</td>
</tr><tr></tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.bmqb.com/jobs" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bmqb.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://codepilot.ai" target="_blank" rel="noopener noreferrer">
<img width="148px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/codepilot.png">
</a>
</td>
</tr><tr></tr>
</tbody>
</table>
<!--gold end-->
<h3 align="center">Sponsors via <a href="https://opencollective.com/vuejs">Open Collective</a></h3>
<h4 align="center">Platinum</h4>
<a href="https://opencollective.com/vuejs/tiers/platinum-sponsors/0/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/platinum-sponsors/0/avatar.svg"></a>
<a href="https://opencollective.com/vuejs/tiers/platinum-sponsors/1/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/platinum-sponsors/1/avatar.svg"></a>
<h4 align="center">Gold</h4>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/0/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/0/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/1/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/1/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/2/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/2/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/3/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/3/avatar.svg" height="60px"></a>
<a href="https://opencollective.com/vuejs/tiers/gold-sponsors/4/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/vuejs/tiers/gold-sponsors/4/avatar.svg" height="60px"></a>
---
## Introduction
Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for building user interfaces. It is designed from the ground up to be incrementally adoptable, and can easily scale between a library and a framework depending on different use cases. It consists of an approachable core library that focuses on the view layer only, and an ecosystem of supporting libraries that helps you tackle complexity in large Single-Page Applications.
#### Browser Compatibility
Vue.js supports all browsers that are [ES5-compliant](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported).
## Ecosystem
| Project | Status | Description |
|---------|--------|-------------|
| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing |
| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management |
| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding |
| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack |
| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support |
| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API |
| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration |
| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension |
[vue-router]: https://github.com/vuejs/vue-router
[vuex]: https://github.com/vuejs/vuex
[vue-cli]: https://github.com/vuejs/vue-cli
[vue-loader]: https://github.com/vuejs/vue-loader
[vue-server-renderer]: https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer
[vue-class-component]: https://github.com/vuejs/vue-class-component
[vue-rx]: https://github.com/vuejs/vue-rx
[vue-devtools]: https://github.com/vuejs/vue-devtools
[vue-router-status]: https://img.shields.io/npm/v/vue-router.svg
[vuex-status]: https://img.shields.io/npm/v/vuex.svg
[vue-cli-status]: https://img.shields.io/npm/v/vue-cli.svg
[vue-loader-status]: https://img.shields.io/npm/v/vue-loader.svg
[vue-server-renderer-status]: https://img.shields.io/npm/v/vue-server-renderer.svg
[vue-class-component-status]: https://img.shields.io/npm/v/vue-class-component.svg
[vue-rx-status]: https://img.shields.io/npm/v/vue-rx.svg
[vue-devtools-status]: https://img.shields.io/chrome-web-store/v/nhdogjmejiglipccpnnnanhbledajbpd.svg
[vue-router-package]: https://npmjs.com/package/vue-router
[vuex-package]: https://npmjs.com/package/vuex
[vue-cli-package]: https://npmjs.com/package/vue-cli
[vue-loader-package]: https://npmjs.com/package/vue-loader
[vue-server-renderer-package]: https://npmjs.com/package/vue-server-renderer
[vue-class-component-package]: https://npmjs.com/package/vue-class-component
[vue-rx-package]: https://npmjs.com/package/vue-rx
[vue-devtools-package]: https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
## Documentation
To check out [live examples](https://vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://vuejs.org).
## Questions
For questions and support please use the [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests.
## Issues
Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
## Stay In Touch
- [Twitter](https://twitter.com/vuejs)
- [Blog](https://medium.com/the-vue-point)
- [Job Board](https://vuejobs.com/?ref=vuejs)
## Contribution
Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md) before making a pull request. If you have a Vue-related project/component/tool, add it with a pull request to [this curated list](https://github.com/vuejs/awesome-vue)!
Thank you to all the people who already contributed to Vue!
<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
## License
[MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2013-present, Yuxi (Evan) You

View File

@ -0,0 +1,15 @@
form {
margin-bottom: 15px;
}
td.hidden {
color: #ccc;
}
table.filtered td.item {
background-color: #FFFFBF;
}
table.filtered td.item.hidden {
background-color: transparent;
}

View File

@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script src="../../dist/vue.min.js"></script>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="demo.css">
</head>
<body>
<div id="el">
<h1>Rendering Dynamic Big Table</h1>
<p>Reference: <a href="http://insin.github.io/ui-lib-samples/large-datasets/index.html">insin/ui-lib-samples/large-datasets</a></p>
<p>
<span>{{ rows }} x {{ cols }}, {{ optimized ? 'with' : 'without' }} optimization. {{ msg }}</span>
</p>
<p>
<button v-if="optimized" @click="loadBase">Disable optimization</button>
<button v-else @click="loadOptimized">Enable optimization (Object.freeze)</button>
<button @click="unmount">Unmount</button>
<button @click="rerender">Rerender with fresh data</button>
</p>
<form>
<strong>Filter Data</strong>:
<input type="text" v-model="filter">
<!--
If the user is filtering the data, we want to offer some insight into
the breadth of the filtering.
-->
<span v-if="filter">
&mdash;
Filtering <strong>{{ filter }}</strong>
over {{ dataPoints }} data points,
{{ visibleCount() }} found.
</span>
</form>
<table width="100%" cellspacing="2" :class="{ filtered: filter }">
<tr v-for="row in grid">
<th>{{ row.id }}</th>
<td v-for="item in row.items"
class="item"
:class="{ hidden: !matches(item) }">
{{ item.value }}
</td>
</tr>
</table>
</div>
<script>
var ROWS = 1000
var COLS = 10
var OPTIMIZED = window.location.hash === '#optimized'
window.onhashchange = function () {
window.location.reload()
}
function generateGrid( rowCount, columnCount ) {
var valuePoints = [
"Daenerys", "Jon", "Sansa", "Arya", "Stannis", "Gregor", "Tyrion",
"Theon", "Joffrey", "Ramsay", "Cersei", "Bran", "Margaery",
"Melisandre", "Daario", "Jamie", "Eddard", "Myrcella", "Robb",
"Jorah", "Petyr", "Tommen", "Sandor", "Oberyn", "Drogo", "Ygritte"
]
var valueIndex = 0
var grid = []
for ( var r = 0; r < rowCount; r++ ) {
var row = {
id: r,
items: []
}
for ( var c = 0; c < columnCount; c++ ) {
row.items.push({
id: ( r + "-" + c ),
value: valuePoints[ valueIndex ]
})
if ( ++valueIndex >= valuePoints.length ) {
valueIndex = 0
}
}
grid.push(row)
}
return OPTIMIZED ? Object.freeze(grid) : grid
}
var grid = generateGrid(ROWS, COLS)
var s = window.performance.now()
console.profile('a')
var vm = new Vue({
el: '#el',
data: {
cols: COLS,
rows: ROWS,
optimized: OPTIMIZED,
msg: 'loading...',
grid: grid,
dataPoints: grid.length * grid[0].items.length,
filter: ''
},
methods: {
matches: function (item) {
return item.value.toLowerCase().indexOf(this.filter.toLowerCase()) > -1
},
visibleCount: function () {
var count = 0
var grid = this.grid
for (var i = 0, l = grid.length; i < l; i++) {
var row = grid[i].items
for (var j = 0, k = row.length; j < k; j++) {
var item = row[j]
var matched = !this.filter || this.matches(item)
if (matched) {
count++
}
}
}
return count
},
loadBase: function () {
window.location.hash = ''
},
loadOptimized: function () {
window.location.hash = '#optimized'
},
unmount: function () {
console.profile('unmount')
var s = window.performance.now()
this.grid = []
setTimeout(function () {
vm.msg = 'umount took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
console.profileEnd('unmount')
}, 0)
},
rerender: function () {
var grid = generateGrid(1000, 10)
var s = window.performance.now()
console.profile('rerender')
this.grid = grid
setTimeout(function () {
vm.msg = 'rerender took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
console.profileEnd('rerender')
}, 0)
}
}
})
console.profileEnd('a')
setTimeout(function () {
vm.msg = 'initial render took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
}, 0)
</script>
</body>
</html>

View File

@ -0,0 +1,553 @@
@font-face {
font-family: octicons-anchor;
src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');
}
body {
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
text-size-adjust: 100%;
color: #333;
font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
font-size: 16px;
line-height: 1.6;
word-wrap: break-word;
padding: 1em;
}
a {
background-color: transparent;
}
a:active,
a:hover {
outline: 0;
}
strong {
font-weight: bold;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
img {
border: 0;
}
hr {
box-sizing: content-box;
height: 0;
}
pre {
overflow: auto;
}
code,
kbd,
pre {
font-family: monospace, monospace;
font-size: 1em;
}
input {
color: inherit;
font: inherit;
margin: 0;
}
html input[disabled] {
cursor: default;
}
input {
line-height: normal;
}
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
* {
box-sizing: border-box;
}
input {
font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
}
a {
color: #4078c0;
text-decoration: none;
}
a:hover,
a:active {
text-decoration: underline;
}
hr {
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid #ddd;
}
hr:before {
display: table;
content: "";
}
hr:after {
display: table;
clear: both;
content: "";
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 15px;
margin-bottom: 15px;
line-height: 1.1;
}
h1 {
font-size: 30px;
}
h1:first-child {
margin-top: 0;
}
h2 {
font-size: 21px;
}
h3 {
font-size: 16px;
}
h4 {
font-size: 14px;
}
h5 {
font-size: 12px;
}
h6 {
font-size: 11px;
}
blockquote {
margin: 0;
}
ul,
ol {
padding: 0;
margin-top: 0;
margin-bottom: 0;
}
ol ol,
ul ol {
list-style-type: lower-roman;
}
ul ul ol,
ul ol ol,
ol ul ol,
ol ol ol {
list-style-type: lower-alpha;
}
dd {
margin-left: 0;
}
code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
pre {
margin-top: 0;
margin-bottom: 0;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
.octicon {
font: normal normal normal 16px/1 octicons-anchor;
display: inline-block;
text-decoration: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.octicon-link:before {
content: '\f05c';
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
a:not([href]) {
cursor: pointer;
text-decoration: none;
}
.anchor {
position: absolute;
top: 0;
left: 0;
display: block;
padding-right: 6px;
padding-left: 30px;
margin-left: -30px;
}
.anchor:focus {
outline: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
position: relative;
margin-top: 1em;
margin-bottom: 16px;
font-weight: bold;
line-height: 1.4;
}
h1 .octicon-link,
h2 .octicon-link,
h3 .octicon-link,
h4 .octicon-link,
h5 .octicon-link,
h6 .octicon-link {
display: none;
color: #000;
vertical-align: middle;
}
h1:hover .anchor,
h2:hover .anchor,
h3:hover .anchor,
h4:hover .anchor,
h5:hover .anchor,
h6:hover .anchor {
padding-left: 8px;
margin-left: -30px;
text-decoration: none;
}
h1:hover .anchor .octicon-link,
h2:hover .anchor .octicon-link,
h3:hover .anchor .octicon-link,
h4:hover .anchor .octicon-link,
h5:hover .anchor .octicon-link,
h6:hover .anchor .octicon-link {
display: inline-block;
}
h1 {
padding-bottom: 0.3em;
font-size: 2.25em;
line-height: 1.2;
border-bottom: 1px solid #eee;
}
h1 .anchor {
line-height: 1;
}
h2 {
padding-bottom: 0.3em;
font-size: 1.75em;
line-height: 1.225;
border-bottom: 1px solid #eee;
}
h2 .anchor {
line-height: 1;
}
h3 {
font-size: 1.5em;
line-height: 1.43;
}
h3 .anchor {
line-height: 1.2;
}
h4 {
font-size: 1.25em;
}
h4 .anchor {
line-height: 1.2;
}
h5 {
font-size: 1em;
}
h5 .anchor {
line-height: 1.1;
}
h6 {
font-size: 1em;
color: #777;
}
h6 .anchor {
line-height: 1.1;
}
p,
blockquote,
ul,
ol,
dl,
table,
pre {
margin-top: 0;
margin-bottom: 16px;
}
hr {
height: 4px;
padding: 0;
margin: 16px 0;
background-color: #e7e7e7;
border: 0 none;
}
ul,
ol {
padding-left: 2em;
}
ul ul,
ul ol,
ol ol,
ol ul {
margin-top: 0;
margin-bottom: 0;
}
li>p {
margin-top: 16px;
}
dl {
padding: 0;
}
dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: bold;
}
dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
blockquote {
padding: 0 15px;
color: #777;
border-left: 4px solid #ddd;
}
blockquote>:first-child {
margin-top: 0;
}
blockquote>:last-child {
margin-bottom: 0;
}
table {
display: block;
width: 100%;
overflow: auto;
word-break: normal;
word-break: keep-all;
}
table th {
font-weight: bold;
}
table th,
table td {
padding: 6px 13px;
border: 1px solid #ddd;
}
table tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
img {
max-width: 100%;
box-sizing: border-box;
}
code {
padding: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
margin: 0;
font-size: 85%;
background-color: rgba(0,0,0,0.04);
border-radius: 3px;
}
code:before,
code:after {
letter-spacing: -0.2em;
content: "\00a0";
}
pre>code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.highlight {
margin-bottom: 16px;
}
.highlight pre,
pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #f7f7f7;
border-radius: 3px;
}
.highlight pre {
margin-bottom: 0;
word-break: normal;
}
pre {
word-wrap: normal;
}
pre code {
display: inline;
max-width: initial;
padding: 0;
margin: 0;
overflow: initial;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
pre code:before,
pre code:after {
content: normal;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #555;
vertical-align: middle;
background-color: #fcfcfc;
border: solid 1px #ccc;
border-bottom-color: #bbb;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb;
}
kbd {
display: inline-block;
padding: 3px 5px;
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
line-height: 10px;
color: #555;
vertical-align: middle;
background-color: #fcfcfc;
border: solid 1px #ccc;
border-bottom-color: #bbb;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb;
}
.task-list-item {
list-style-type: none;
}
.task-list-item+.task-list-item {
margin-top: 3px;
}
.task-list-item input {
margin: 0 0.35em 0.25em -1.6em;
vertical-align: middle;
}
:checked+.radio-label {
z-index: 1;
position: relative;
border-color: #4078c0;
}

View File

@ -0,0 +1,211 @@
var ENV = ENV || (function() {
var first = true;
var counter = 0;
var data;
var _base;
(_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
return padding.repeat((toLength - this.length) / padding.length).concat(this);
});
function formatElapsed(value) {
var str = parseFloat(value).toFixed(2);
if (value > 60) {
minutes = Math.floor(value / 60);
comps = (value % 60).toFixed(2).split('.');
seconds = comps[0].lpad('0', 2);
ms = comps[1];
str = minutes + ":" + seconds + "." + ms;
}
return str;
}
function getElapsedClassName(elapsed) {
var className = 'Query elapsed';
if (elapsed >= 10.0) {
className += ' warn_long';
}
else if (elapsed >= 1.0) {
className += ' warn';
}
else {
className += ' short';
}
return className;
}
function countClassName(queries) {
var countClassName = "label";
if (queries >= 20) {
countClassName += " label-important";
}
else if (queries >= 10) {
countClassName += " label-warning";
}
else {
countClassName += " label-success";
}
return countClassName;
}
function updateQuery(object) {
if (!object) {
object = {};
}
var elapsed = Math.random() * 15;
object.elapsed = elapsed;
object.formatElapsed = formatElapsed(elapsed);
object.elapsedClassName = getElapsedClassName(elapsed);
object.query = "SELECT blah FROM something";
object.waiting = Math.random() < 0.5;
if (Math.random() < 0.2) {
object.query = "<IDLE> in transaction";
}
if (Math.random() < 0.1) {
object.query = "vacuum";
}
return object;
}
function cleanQuery(value) {
if (value) {
value.formatElapsed = "";
value.elapsedClassName = "";
value.query = "";
value.elapsed = null;
value.waiting = null;
} else {
return {
query: "***",
formatElapsed: "",
elapsedClassName: ""
};
}
}
function generateRow(object, keepIdentity, counter) {
var nbQueries = Math.floor((Math.random() * 10) + 1);
if (!object) {
object = {};
}
object.lastMutationId = counter;
object.nbQueries = nbQueries;
if (!object.lastSample) {
object.lastSample = {};
}
if (!object.lastSample.topFiveQueries) {
object.lastSample.topFiveQueries = [];
}
if (keepIdentity) {
// for Angular optimization
if (!object.lastSample.queries) {
object.lastSample.queries = [];
for (var l = 0; l < 12; l++) {
object.lastSample.queries[l] = cleanQuery();
}
}
for (var j in object.lastSample.queries) {
var value = object.lastSample.queries[j];
if (j <= nbQueries) {
updateQuery(value);
} else {
cleanQuery(value);
}
}
} else {
object.lastSample.queries = [];
for (var j = 0; j < 12; j++) {
if (j < nbQueries) {
var value = updateQuery(cleanQuery());
object.lastSample.queries.push(value);
} else {
object.lastSample.queries.push(cleanQuery());
}
}
}
for (var i = 0; i < 5; i++) {
var source = object.lastSample.queries[i];
object.lastSample.topFiveQueries[i] = source;
}
object.lastSample.nbQueries = nbQueries;
object.lastSample.countClassName = countClassName(nbQueries);
return object;
}
function getData(keepIdentity) {
var oldData = data;
if (!keepIdentity) { // reset for each tick when !keepIdentity
data = [];
for (var i = 1; i <= ENV.rows; i++) {
data.push({ dbname: 'cluster' + i, query: "", formatElapsed: "", elapsedClassName: "" });
data.push({ dbname: 'cluster' + i + ' slave', query: "", formatElapsed: "", elapsedClassName: "" });
}
}
if (!data) { // first init when keepIdentity
data = [];
for (var i = 1; i <= ENV.rows; i++) {
data.push({ dbname: 'cluster' + i });
data.push({ dbname: 'cluster' + i + ' slave' });
}
oldData = data;
}
for (var i in data) {
var row = data[i];
if (!keepIdentity && oldData && oldData[i]) {
row.lastSample = oldData[i].lastSample;
}
if (!row.lastSample || Math.random() < ENV.mutations()) {
counter = counter + 1;
if (!keepIdentity) {
row.lastSample = null;
}
generateRow(row, keepIdentity, counter);
} else {
data[i] = oldData[i];
}
}
first = false;
return {
toArray: function() {
return data;
}
};
}
var mutationsValue = 0.5;
function mutations(value) {
if (value) {
mutationsValue = value;
return mutationsValue;
} else {
return mutationsValue;
}
}
var body = document.querySelector('body');
var theFirstChild = body.firstChild;
var sliderContainer = document.createElement( 'div' );
sliderContainer.style.cssText = "display: flex";
var slider = document.createElement('input');
var text = document.createElement('label');
text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
text.id = "ratioval";
slider.setAttribute("type", "range");
slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px';
slider.addEventListener('change', function(e) {
ENV.mutations(e.target.value / 100);
document.querySelector('#ratioval').innerHTML = 'mutations : ' + (ENV.mutations() * 100).toFixed(0) + '%';
});
sliderContainer.appendChild( text );
sliderContainer.appendChild( slider );
body.insertBefore( sliderContainer, theFirstChild );
return {
generateData: getData,
rows: 50,
timeout: 0,
mutations: mutations
};
})();

View File

@ -0,0 +1,14 @@
var app = new Vue({
el: '#app',
data: {
databases: []
}
})
function loadSamples() {
app.databases = Object.freeze(ENV.generateData().toArray());
Monitoring.renderRate.ping();
setTimeout(loadSamples, ENV.timeout);
}
loadSamples()

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="dbmon vue" />
<link href="./lib/styles.css" rel="stylesheet" type="text/css" />
<title>dbmon (Vue)</title>
</head>
<body>
<h2>
Reference: <a href="http://mathieuancelin.github.io/js-repaint-perfs/">js-repaint-perfs</a>
</h2>
<div id="app">
<table class="table table-striped lastest-data">
<tbody>
<tr v-for="db in databases">
<td class="dbname">{{db.dbname}}</td>
<td class="query-count">
<span :class="db.lastSample.countClassName">{{db.lastSample.nbQueries}}</span>
</td>
<td v-for="q in db.lastSample.topFiveQueries" :class="'Query ' + q.elapsedClassName">
{{q.formatElapsed}}
<div class="popover left">
<div class="popover-content">{{q.query}}</div>
<div class="arrow"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<script src="./ENV.js"></script>
<script src="./lib/memory-stats.js"></script>
<script src="./lib/monitor.js"></script>
<script src="../../dist/vue.min.js"></script>
<script src="./app.js"></script>
</body>
</html>

View File

@ -0,0 +1,98 @@
/**
* @author mrdoob / http://mrdoob.com/
* @author jetienne / http://jetienne.com/
* @author paulirish / http://paulirish.com/
*/
var MemoryStats = function (){
var msMin = 100;
var msMax = 0;
var container = document.createElement( 'div' );
container.id = 'stats';
container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
var msDiv = document.createElement( 'div' );
msDiv.id = 'ms';
msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
container.appendChild( msDiv );
var msText = document.createElement( 'div' );
msText.id = 'msText';
msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
msText.innerHTML= 'Memory';
msDiv.appendChild( msText );
var msGraph = document.createElement( 'div' );
msGraph.id = 'msGraph';
msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
msDiv.appendChild( msGraph );
while ( msGraph.children.length < 74 ) {
var bar = document.createElement( 'span' );
bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
msGraph.appendChild( bar );
}
var updateGraph = function ( dom, height, color ) {
var child = dom.appendChild( dom.firstChild );
child.style.height = height + 'px';
if( color ) child.style.backgroundColor = color;
}
var perf = window.performance || {};
// polyfill usedJSHeapSize
if (!perf.memory){
perf.memory = { usedJSHeapSize : 0 };
}
// support of the API?
if( perf.memory.totalJSHeapSize === 0 ){
console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .')
}
// TODO, add a sanity check to see if values are bucketed.
// If so, remind user to adopt the --enable-precise-memory-info flag.
// open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info
var lastTime = Date.now();
var lastUsedHeap= perf.memory.usedJSHeapSize;
return {
domElement: container,
update: function () {
// refresh only 30time per second
if( Date.now() - lastTime < 1000/30 ) return;
lastTime = Date.now()
var delta = perf.memory.usedJSHeapSize - lastUsedHeap;
lastUsedHeap = perf.memory.usedJSHeapSize;
var color = delta < 0 ? '#830' : '#131';
var ms = perf.memory.usedJSHeapSize;
msMin = Math.min( msMin, ms );
msMax = Math.max( msMax, ms );
msText.textContent = "Mem: " + bytesToSize(ms, 2);
var normValue = ms / (30*1024*1024);
var height = Math.min( 30, 30 - normValue * 30 );
updateGraph( msGraph, height, color);
function bytesToSize( bytes, nFractDigit ){
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return 'n/a';
nFractDigit = nFractDigit !== undefined ? nFractDigit : 0;
var precision = Math.pow(10, nFractDigit);
var i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes*precision / Math.pow(1024, i))/precision + ' ' + sizes[i];
};
}
}
};

View File

@ -0,0 +1,60 @@
var Monitoring = Monitoring || (function() {
var stats = new MemoryStats();
stats.domElement.style.position = 'fixed';
stats.domElement.style.right = '0px';
stats.domElement.style.bottom = '0px';
document.body.appendChild( stats.domElement );
requestAnimationFrame(function rAFloop(){
stats.update();
requestAnimationFrame(rAFloop);
});
var RenderRate = function () {
var container = document.createElement( 'div' );
container.id = 'stats';
container.style.cssText = 'width:150px;opacity:0.9;cursor:pointer;position:fixed;right:80px;bottom:0px;';
var msDiv = document.createElement( 'div' );
msDiv.id = 'ms';
msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
container.appendChild( msDiv );
var msText = document.createElement( 'div' );
msText.id = 'msText';
msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
msText.innerHTML= 'Repaint rate: 0/sec';
msDiv.appendChild( msText );
var bucketSize = 20;
var bucket = [];
var lastTime = Date.now();
return {
domElement: container,
ping: function () {
var start = lastTime;
var stop = Date.now();
var rate = 1000 / (stop - start);
bucket.push(rate);
if (bucket.length > bucketSize) {
bucket.shift();
}
var sum = 0;
for (var i = 0; i < bucket.length; i++) {
sum = sum + bucket[i];
}
msText.textContent = "Repaint rate: " + (sum / bucket.length).toFixed(2) + "/sec";
lastTime = stop;
}
}
};
var renderRate = new RenderRate();
document.body.appendChild( renderRate.domElement );
return {
memoryStats: stats,
renderRate: renderRate
};
})();

View File

@ -0,0 +1,26 @@
body {color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;margin:0;}
label {display:inline-block;font-weight:700;margin-bottom:5px;}
input[type=range] {display:block;width:100%;}
table {border-collapse:collapse;border-spacing:0;}
:before,:after {box-sizing: border-box;}
.table > thead > tr > th,.table > tbody > tr > th,.table > tfoot > tr > th,.table > thead > tr > td,.table > tbody > tr > td,.table > tfoot > tr > td {border-top:1px solid #ddd;line-height:1.42857143;padding:8px;vertical-align:top;}
.table {width:100%;}
.table-striped > tbody > tr:nth-child(odd) > td,.table-striped > tbody > tr:nth-child(odd) > th {background:#f9f9f9;}
.label {border-radius:.25em;color:#fff;display:inline;font-size:75%;font-weight:700;line-height:1;padding:.2em .6em .3em;text-align:center;vertical-align:baseline;white-space:nowrap;}
.label-success {background-color:#5cb85c;}
.label-warning {background-color:#f0ad4e;}
.popover {background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,.2);display:none;left:0;max-width:276px;padding:1px;position:absolute;text-align:left;top:0;white-space:normal;z-index:1010;}
.popover>.arrow:after {border-width:10px;content:"";}
.popover.left {margin-left:-10px;}
.popover.left > .arrow {border-right-width:0;border-left-color:rgba(0,0,0,.25);margin-top:-11px;right:-11px;top:50%;}
.popover.left > .arrow:after {border-left-color:#fff;border-right-width:0;bottom:-10px;content:" ";right:1px;}
.popover > .arrow {border-width:11px;}
.popover > .arrow,.popover>.arrow:after {border-color:transparent;border-style:solid;display:block;height:0;position:absolute;width:0;}
.popover-content {padding:9px 14px;}
.Query {position:relative;}
.Query:hover .popover {display:block;left:-100%;width:100%;}

View File

@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue benchmark</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/lodash/4.10.0/lodash.min.js"></script>
<script src="../../dist/vue.min.js"></script>
<style>
.danger {
background-color: red;
}
</style>
<script type="text/x-template" id="t">
<div>
<h1>{{ total }} Components</h1>
<p>{{ action }} took {{time}}ms.</p>
<button @click="shuffle">shuffle</button>
<button @click="add">add</button>
<table class="table table-hover table-striped test-data">
<row v-for="item in items" :key="item.id"
:class="{ danger: item.id === selected }"
:item="item"
@select="select(item)"
@remove="remove(item)">
</row>
</table>
</div>
</script>
<script type="text/x-template" id="row">
<tr>
<td class="col-md-1">{{item.id}}</td>
<td class="col-md-4">
<a @click="$emit('select')">{{item.label}}</a>
</td>
<td class="col-md-1">
<button @click="$emit('remove')">remove</button>
</td>
</tr>
</script>
<div id="el">
</div>
<script>
var total = 1000
var items = []
for (var i = 0; i < total; i++) {
items.push({
id: i,
label: String(Math.random()).slice(0, 5)
})
}
var s = window.performance.now()
console.profile('render')
var vm = new Vue({
el: '#el',
template: '#t',
data: {
total: total,
time: 0,
action: 'Render',
items: items,
selected: null
},
methods: {
shuffle: monitor('shuffle', function () {
this.items = _.shuffle(this.items)
}),
add: monitor('add', function () {
this.items.push({
id: total++,
label: String(Math.random()).slice(0, 5)
})
}),
select: monitor('select', function (item) {
this.selected = item.id
}),
remove: monitor('remove', function (item) {
this.items.splice(this.items.indexOf(item), 1)
})
},
components: {
row: {
props: ['item'],
template: '#row'
}
}
})
setTimeout(function () {
vm.time = window.performance.now() - s
console.profileEnd('render')
}, 0)
function monitor (action, fn) {
return function () {
var s = window.performance.now()
fn.apply(this, arguments)
Vue.nextTick(function () {
vm.action = action
vm.time = window.performance.now() - s
})
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,13 @@
# Vue.js SSR benchmark
This benchmark renders a table of 1000 rows with 10 columns (10k components), with around 30k normal elements on the page. Note this is not something likely to be seen in a typical app. This benchmark is mostly for stress/regression testing and comparing between `renderToString` and `renderToStream`.
To view the results follow the run section. Note that the overall completion time for the results are variable, this is due to other system related variants at run time (available memory, processing power, etc). In ideal circumstances both should finish within similar results.
`renderToStream` pipes the content through a stream which provides considerable performance benefits (faster time-to-first-byte and non-event-loop-blocking) over `renderToString`. This can be observed through the benchmark.
### run
``` bash
npm run bench:ssr
```

View File

@ -0,0 +1,59 @@
'use strict'
const self = (global || root)
self.performance = {
now: function () {
var hrtime = process.hrtime()
return ((hrtime[0] * 1000000 + hrtime[1] / 1000) / 1000)
}
}
function generateGrid (rowCount, columnCount) {
var grid = []
for (var r = 0; r < rowCount; r++) {
var row = { id: r, items: [] }
for (var c = 0; c < columnCount; c++) {
row.items.push({ id: (r + '-' + c) })
}
grid.push(row)
}
return grid
}
const gridData = generateGrid(1000, 10)
module.exports = {
template: '<div><h1>{{ Math.random() }}</h1><my-table></my-table></div>',
components: {
myTable: {
data: function () {
return {
grid: gridData
}
},
// template: '<table><tr v-for="row in grid"><th>123</th><td v-for="item in row.items">{{ item.id }}</td></tr></table>',
template: '<table width="100%" cellspacing="2"><row v-for="row in grid" :row="row"></row></table>',
components: {
row: {
props: ['row'],
template: '<tr><th>{{ Math.random() }}</th><column v-for="item in row.items"></column></tr>',
components: {
column: {
template: '<td class="item">' +
// 25 plain elements for each cell
'<ul class="yoyo">' +
`<li v-for="i in 5" :class="'hihi' + i">` +
`<span :id="i + '_' + j" v-for="j in 5">fsefs</span>` +
'</li>' +
'</ul>' +
'</td>'
}
}
}
}
}
}
}

View File

@ -0,0 +1,31 @@
/* eslint-disable no-unused-vars */
'use strict'
process.env.NODE_ENV = 'production'
const Vue = require('../../dist/vue.runtime.common.js')
const createRenderer = require('../../packages/vue-server-renderer').createRenderer
const renderToStream = createRenderer().renderToStream
const gridComponent = require('./common.js')
console.log('--- renderToStream --- ')
const self = (global || root)
const s = self.performance.now()
const stream = renderToStream(new Vue(gridComponent))
let str = ''
let first
let complete
stream.once('data', () => {
first = self.performance.now() - s
})
stream.on('data', chunk => {
str += chunk
})
stream.on('end', () => {
complete = self.performance.now() - s
console.log(`first chunk: ${first.toFixed(2)}ms`)
console.log(`complete: ${complete.toFixed(2)}ms`)
console.log()
})

View File

@ -0,0 +1,19 @@
'use strict'
process.env.NODE_ENV = 'production'
const Vue = require('../../dist/vue.runtime.common.js')
const createRenderer = require('../../packages/vue-server-renderer').createRenderer
const renderToString = createRenderer().renderToString
const gridComponent = require('./common.js')
console.log('--- renderToString --- ')
const self = (global || root)
self.s = self.performance.now()
renderToString(new Vue(gridComponent), (err, res) => {
if (err) throw err
// console.log(res)
console.log('Complete time: ' + (self.performance.now() - self.s).toFixed(2) + 'ms')
console.log()
})

View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>vue.js version</title>
<script src="https://cdn.jsdelivr.net/stats.js/r11/stats.min.js"></script>
<script src="../../dist/vue.min.js"></script>
<style>
html, body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
svg {
width: 800px;
height: 600px;
}
</style>
</head>
<body>
<h1>Animating 1000 SVG dots</h1>
<div id="app">
<p>
<button @click="toggleOptimization">
{{ optimized ? 'disable' : 'enable' }} optimization (Object.freeze)
</button>
</p>
<svg>
<circle v-for='point in model.points' :cx='point.x' :cy='point.y' r='2px' fill='#FC309D'></circle>
</svg>
</div>
<script type="text/javascript" charset="utf-8">
var stats = new Stats()
stats.setMode(0)
stats.domElement.style.position = 'absolute'
stats.domElement.style.right = '0px'
stats.domElement.style.top = '0px'
document.body.appendChild(stats.domElement)
var WIDTH = 800
var HEIGHT = 600
new Vue({
el: '#app',
data: {
model: createModel(1000),
optimized: false
},
created: function () {
var self = this
requestAnimationFrame(render)
stats.begin()
function render () {
stats.end()
stats.begin()
requestAnimationFrame(render)
self.model.step()
if (self.optimized) {
self.$forceUpdate()
}
}
},
methods: {
toggleOptimization: function () {
this.model = this.optimized
? createModel(1000)
: Object.freeze(createModel(1000))
this.optimized = !this.optimized
}
}
});
function createModel (count) {
var points = []
for (var i = 0; i < count; ++i) {
points.push({
x: Math.random() * WIDTH,
y: Math.random() * HEIGHT,
vx: Math.random() * 4 - 2,
vy: Math.random() * 4 - 2
})
}
return {
points: points,
step: step
}
function step () {
points.forEach(move)
}
function move (p) {
if (p.x > WIDTH || p.x < 0) p.vx *= -1
if (p.y > HEIGHT || p.y < 0) p.vy *= -1
p.y += p.vy
p.x += p.vx
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue benchmark</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0 10px;
font-family: sans-serif;
}
#fps {
position: fixed;
top: 0px;
right: 0px;
padding: 32px;
font-size: 32px;
text-align: right;
}
* {
box-sizing: border-box;
}
.server-uptime {
display: block;
overflow: hidden;
margin: 0 auto;
width: 50%;
}
.server-uptime + .server-uptime {
margin: 20px auto 0 auto;
border-top: 1px solid #999;
}
.days {
display: flex;
flex-direction: row;
flex-flow: wrap;
}
.uptime-day {
display: flex;
}
span.uptime-day-status {
width: 10px;
height: 10px;
margin: 1px;
}
.hover {
display: none;
}
.uptime-day-status:hover + .hover {
display: flex;
position: absolute;
margin-top: -35px;
margin-left: -30px;
border-radius: 4px;
color: #eee;
background-color: #333;
padding: 10px;
font-size: 11px;
}
</style>
</head>
<body>
<p>Reference: <a href="https://github.com/tildeio/glimmer/blob/master/packages/glimmer-demos/lib/uptime.ts">Ember Glimmer 2 demo</a></p>
<div id="app">
<p>FPS: {{ fps }}</p>
<button @click="toggle">{{ playing ? 'pause' : 'play' }}</button>
<server-uptime
v-for="server in servers"
:key="server.name"
:name="server.name"
:days="server.days">
</server-uptime>
</div>
<script src="../../dist/vue.min.js"></script>
<script>
// functional components are prefect for small, presentational components
// and they are much more efficient than stateful ones.
Vue.component('uptime-day', {
props: ['day'],
functional: true,
render (h, ctx) {
var day = ctx.props.day
return h('div', { staticClass: 'uptime-day'}, [
h('span', { staticClass: 'uptime-day-status', style: { backgroundColor: day.up ? '#8cc665' : '#ccc' } }),
h('span', { staticClass: 'hover' }, [day.number + ': ' + day.up ? 'Servers operational!' : 'Red alert!'])
])
}
})
Vue.component('server-uptime', {
props: ['name', 'days'],
computed: {
upDays () {
return this.days.reduce(function (upDays, day) {
return upDays += (day.up ? 1 : 0)
}, 0)
},
maxStreak () {
var streak = this.days.reduce(([max, streak], day) => {
if (day.up && streak + 1 > max) {
return [streak + 1, streak + 1]
} else if (day.up) {
return [max, streak + 1]
} else {
return [max, 0]
}
}, [0, 0])
return streak.max
}
},
template: `
<div class="server-uptime">
<h1>{{name}}</h1>
<h2>{{upDays}} Days Up</h2>
<h2>Biggest Streak: {{maxStreak}}</h2>
<div class="days">
<uptime-day
v-for="day in days"
:key="day.number"
:day="day">
</uptime-day>
</div>
</div>
`
})
function generateServer (name) {
var days = []
for (var i=0; i<=364; i++) {
var up = Math.random() > 0.2
days.push({ number: i, up })
}
return { name, days }
}
function generateServers () {
return [
generateServer("Stefan's Server"),
generateServer("Godfrey's Server"),
generateServer("Yehuda's Server")
]
}
var s = window.performance.now()
var app = new Vue({
el: '#app',
data: {
fps: 0,
playing: false,
servers: Object.freeze(generateServers())
},
methods: {
toggle () {
this.playing = !this.playing
if (this.playing) {
update()
} else {
clearTimeout(timeoutId)
}
}
}
})
console.log('initial render: ' + (window.performance.now() - s) + 'ms')
var fpsMeter = {
alpha: 2/121,
lastValue: null,
push (dataPoint) {
if (this.lastValue) {
return this.lastValue = this.lastValue + this.alpha * (dataPoint - this.lastValue)
} else {
return this.lastValue = dataPoint
}
}
}
var timeoutId
var lastFrame = null
function update () {
var thisFrame = window.performance.now()
if (lastFrame) {
app.fps = Math.round(fpsMeter.push(1000 / (thisFrame - lastFrame)))
}
app.servers = Object.freeze(generateServers())
timeoutId = setTimeout(update, 0) // not using rAF because that limits us to 60fps!
lastFrame = thisFrame
}
</script>
</body>
</html>

View File

@ -0,0 +1,124 @@
## Explanation of Build Files
| | UMD | CommonJS | ES Module |
| --- | --- | --- | --- |
| **Full** | vue.js | vue.common.js | vue.esm.js |
| **Runtime-only** | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |
| **Full (production)** | vue.min.js | | |
| **Runtime-only (production)** | vue.runtime.min.js | | |
### Terms
- **Full**: builds that contains both the compiler and the runtime.
- **Compiler**: code that is responsible for compiling template strings into JavaScript render functions.
- **Runtime**: code that is responsible for creating Vue instances, rendering and patching virtual DOM, etc. Basically everything minus the compiler.
- **[UMD](https://github.com/umdjs/umd)**: UMD builds can be used directly in the browser via a `<script>` tag. The default file from Unpkg CDN at [https://unpkg.com/vue](https://unpkg.com/vue) is the Runtime + Compiler UMD build (`vue.js`).
- **[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1)**: CommonJS builds are intended for use with older bundlers like [browserify](http://browserify.org/) or [webpack 1](https://webpack.github.io). The default file for these bundlers (`pkg.main`) is the Runtime only CommonJS build (`vue.runtime.common.js`).
- **[ES Module](http://exploringjs.com/es6/ch_modules.html)**: ES module builds are intended for use with modern bundlers like [webpack 2](https://webpack.js.org) or [rollup](http://rollupjs.org/). The default file for these bundlers (`pkg.module`) is the Runtime only ES Module build (`vue.runtime.esm.js`).
### Runtime + Compiler vs. Runtime-only
If you need to compile templates on the fly (e.g. passing a string to the `template` option, or mounting to an element using its in-DOM HTML as the template), you will need the compiler and thus the full build.
When using `vue-loader` or `vueify`, templates inside `*.vue` files are compiled into JavaScript at build time. You don't really need the compiler in the final bundle, and can therefore use the runtime-only build.
Since the runtime-only builds are roughly 30% lighter-weight than their full-build counterparts, you should use it whenever you can. If you wish to use the full build instead, you need to configure an alias in your bundler.
#### Webpack
``` js
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
}
}
````
#### Rollup
``` js
const alias = require('rollup-plugin-alias')
rollup({
// ...
plugins: [
alias({
'vue': 'vue/dist/vue.esm.js'
})
]
})
```
#### Browserify
Add to your project's `package.json`:
``` js
{
// ...
"browser": {
"vue": "vue/dist/vue.common.js"
}
}
```
### Development vs. Production Mode
Development/production modes are hard-coded for the UMD builds: the un-minified files are for development, and the minified files are for production.
CommonJS and ES Module builds are intended for bundlers, therefore we don't provide minified versions for them. You will be responsible for minifying the final bundle yourself.
CommonJS and ES Module builds also preserve raw checks for `process.env.NODE_ENV` to determine the mode they should run in. You should use appropriate bundler configurations to replace these environment variables in order to control which mode Vue will run in. Replacing `process.env.NODE_ENV` with string literals also allows minifiers like UglifyJS to completely drop the development-only code blocks, reducing final file size.
#### Webpack
Use Webpack's [DefinePlugin](https://webpack.js.org/plugins/define-plugin/):
``` js
var webpack = require('webpack')
module.exports = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
}
```
#### Rollup
Use [rollup-plugin-replace](https://github.com/rollup/rollup-plugin-replace):
``` js
const replace = require('rollup-plugin-replace')
rollup({
// ...
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}).then(...)
```
#### Browserify
Apply a global [envify](https://github.com/hughsk/envify) transform to your bundle.
``` bash
NODE_ENV=production browserify -g envify -e main.js | uglifyjs -c -m > build.js
```

View File

@ -0,0 +1,47 @@
var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha='
/**
* Actual demo
*/
var demo = new Vue({
el: '#demo',
data: {
branches: ['master', 'dev'],
currentBranch: 'master',
commits: null
},
created: function () {
this.fetchData()
},
watch: {
currentBranch: 'fetchData'
},
filters: {
truncate: function (v) {
var newline = v.indexOf('\n')
return newline > 0 ? v.slice(0, newline) : v
},
formatDate: function (v) {
return v.replace(/T|Z/g, ' ')
}
},
methods: {
fetchData: function () {
var xhr = new XMLHttpRequest()
var self = this
xhr.open('GET', apiURL + self.currentBranch)
xhr.onload = function () {
self.commits = JSON.parse(xhr.responseText)
console.log(self.commits[0].html_url)
}
xhr.send()
}
}
})

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Vue.js github commits example</title>
<style>
#demo {
font-family: 'Helvetica', Arial, sans-serif;
}
a {
text-decoration: none;
color: #f66;
}
li {
line-height: 1.5em;
margin-bottom: 20px;
}
.author, .date {
font-weight: bold;
}
</style>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
</head>
<body>
<div id="demo">
<h1>Latest Vue.js Commits</h1>
<template v-for="branch in branches">
<input type="radio"
:id="branch"
:value="branch"
name="branch"
v-model="currentBranch">
<label :for="branch">{{ branch }}</label>
</template>
<p>vuejs/vue@{{ currentBranch }}</p>
<ul>
<li v-for="record in commits">
<a :href="record.html_url" target="_blank" class="commit">{{ record.sha.slice(0, 7) }}</a>
- <span class="message">{{ record.commit.message | truncate }}</span><br>
by <span class="author"><a :href="record.author.html_url" target="_blank">{{ record.commit.author.name }}</a></span>
at <span class="date">{{ record.commit.author.date | formatDate }}</span>
</li>
</ul>
</div>
<script src="app.js"></script>
</body>
</html>

View File

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Vue.js elastic header example</title>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
<script src="http://dynamicsjs.com/lib/dynamics.js"></script>
<link rel="stylesheet" href="style.css">
<!-- template for the component -->
<script type="text/x-template" id="header-view-template">
<div class="draggable-header-view"
@mousedown="startDrag" @touchstart="startDrag"
@mousemove="onDrag" @touchmove="onDrag"
@mouseup="stopDrag" @touchend="stopDrag" @mouseleave="stopDrag">
<svg class="bg" width="320" height="560">
<path :d="headerPath" fill="#3F51B5"></path>
</svg>
<div class="header">
<slot name="header"></slot>
</div>
<div class="content" :style="contentPosition">
<slot name="content"></slot>
</div>
</div>
</script>
</head>
<body>
<div id="app" @touchmove.prevent>
<draggable-header-view>
<template slot="header">
<h1>Elastic Draggable SVG Header</h1>
<p>with <a href="https://vuejs.org">Vue.js</a> + <a href="http://dynamicsjs.com">dynamics.js</a></p>
</template>
<template slot="content">
<p>Note this is just an effect demo - there are of course many additional details if you want to use this in production, e.g. handling responsive sizes, reload threshold and content scrolling. Those are out of scope for this quick little hack. However, the idea is that you can hide them as internal details of a Vue.js component and expose a simple Web-Component-like interface.</p>
</template>
</draggable-header-view>
</div>
<script>
Vue.component('draggable-header-view', {
template: '#header-view-template',
data: function () {
return {
dragging: false,
// quadratic bezier control point
c: { x: 160, y: 160 },
// record drag start point
start: { x: 0, y: 0 }
}
},
computed: {
headerPath: function () {
return 'M0,0 L320,0 320,160' +
'Q' + this.c.x + ',' + this.c.y +
' 0,160'
},
contentPosition: function () {
var dy = this.c.y - 160
var dampen = dy > 0 ? 2 : 4
return {
transform: 'translate3d(0,' + dy / dampen + 'px,0)'
}
}
},
methods: {
startDrag: function (e) {
e = e.changedTouches ? e.changedTouches[0] : e
this.dragging = true
this.start.x = e.pageX
this.start.y = e.pageY
},
onDrag: function (e) {
e = e.changedTouches ? e.changedTouches[0] : e
if (this.dragging) {
this.c.x = 160 + (e.pageX - this.start.x)
// dampen vertical drag by a factor
var dy = e.pageY - this.start.y
var dampen = dy > 0 ? 1.5 : 4
this.c.y = 160 + dy / dampen
}
},
stopDrag: function () {
if (this.dragging) {
this.dragging = false
dynamics.animate(this.c, {
x: 160,
y: 160
}, {
type: dynamics.spring,
duration: 700,
friction: 280
})
}
}
}
})
new Vue({ el: '#app' })
</script>
</body>
</html>

View File

@ -0,0 +1,44 @@
h1 {
font-weight: 300;
font-size: 1.8em;
margin-top: 0;
}
a {
color: #fff;
}
.draggable-header-view {
background-color: #fff;
box-shadow: 0 4px 16px rgba(0,0,0,.15);
width: 320px;
height: 560px;
overflow: hidden;
margin: 30px auto;
position: relative;
font-family: 'Roboto', Helvetica, Arial, sans-serif;
color: #fff;
font-size: 14px;
font-weight: 300;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.draggable-header-view .bg {
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
.draggable-header-view .header, .draggable-header-view .content {
position: relative;
z-index: 1;
padding: 30px;
box-sizing: border-box;
}
.draggable-header-view .header {
height: 160px;
}
.draggable-header-view .content {
color: #333;
line-height: 1.5em;
}

View File

@ -0,0 +1,57 @@
var emailRE = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
// Setup Firebase
var config = {
apiKey: "AIzaSyAi_yuJciPXLFr_PYPeU3eTvtXf8jbJ8zw",
authDomain: "vue-demo-537e6.firebaseapp.com",
databaseURL: "https://vue-demo-537e6.firebaseio.com"
}
firebase.initializeApp(config)
var usersRef = firebase.database().ref('users')
// create Vue app
var app = new Vue({
// element to mount to
el: '#app',
// initial data
data: {
newUser: {
name: '',
email: ''
}
},
// firebase binding
// https://github.com/vuejs/vuefire
firebase: {
users: usersRef
},
// computed property for form validation state
computed: {
validation: function () {
return {
name: !!this.newUser.name.trim(),
email: emailRE.test(this.newUser.email)
}
},
isValid: function () {
var validation = this.validation
return Object.keys(validation).every(function (key) {
return validation[key]
})
}
},
// methods
methods: {
addUser: function () {
if (this.isValid) {
usersRef.push(this.newUser)
this.newUser.name = ''
this.newUser.email = ''
}
},
removeUser: function (user) {
usersRef.child(user['.key']).remove()
}
}
})

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue.js firebase + validation example</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<!-- Vue -->
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
<!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script>
<!-- VueFire -->
<script src="https://unpkg.com/vuefire@1.3.0"></script>
</head>
<body>
<div id="app">
<ul is="transition-group">
<li v-for="user in users" class="user" :key="user['.key']">
<span>{{user.name}} - {{user.email}}</span>
<button v-on:click="removeUser(user)">X</button>
</li>
</ul>
<form id="form" v-on:submit.prevent="addUser">
<input v-model="newUser.name">
<input v-model="newUser.email">
<input type="submit" value="Add User">
</form>
<ul class="errors">
<li v-show="!validation.name">Name cannot be empty.</li>
<li v-show="!validation.email">Please provide a valid email address.</li>
</ul>
</div>
<script src="app.js"></script>
</body>
</html>

View File

@ -0,0 +1,32 @@
body {
font-family: Helvetica, Arial, sans-serif;
}
ul {
padding: 0;
}
.user {
height: 30px;
line-height: 30px;
padding: 10px;
border-top: 1px solid #eee;
overflow: hidden;
transition: all .25s ease;
}
.user:last-child {
border-bottom: 1px solid #eee;
}
.v-enter, .v-leave-to {
height: 0;
padding-top: 0;
padding-bottom: 0;
border-top-width: 0;
border-bottom-width: 0;
}
.errors {
color: #f00;
}

View File

@ -0,0 +1,69 @@
// register the grid component
Vue.component('demo-grid', {
template: '#grid-template',
replace: true,
props: {
data: Array,
columns: Array,
filterKey: String
},
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredData: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var data = this.data
if (filterKey) {
data = data.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
data = data.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
}
})

View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue.js grid component example</title>
<link rel="stylesheet" href="style.css">
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
</head>
<body>
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table v-if="filteredData.length">
<thead>
<tr>
<th v-for="key in columns"
@click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredData">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
<p v-else>No matches found.</p>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
<script src="grid.js"></script>
</body>
</html>

View File

@ -0,0 +1,59 @@
body {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 14px;
color: #444;
}
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}
th {
background-color: #42b983;
color: rgba(255,255,255,0.66);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
td {
background-color: #f9f9f9;
}
th, td {
min-width: 120px;
padding: 10px 20px;
}
th.active {
color: #fff;
}
th.active .arrow {
opacity: 1;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue.js markdown editor example</title>
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/marked@0.3.6"></script>
<script src="https://unpkg.com/lodash@4.16.0"></script>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
</head>
<body>
<div id="editor">
<textarea :value="input" @input="update"></textarea>
<div v-html="compiledMarkdown"></div>
</div>
<script>
new Vue({
el: '#editor',
data: {
input: '# hello'
},
computed: {
compiledMarkdown: function () {
return marked(this.input, { sanitize: true })
}
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value
}, 300)
}
})
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
html, body, #editor {
margin: 0;
height: 100%;
font-family: 'Helvetica Neue', Arial, sans-serif;
color: #333;
}
textarea, #editor div {
display: inline-block;
width: 49%;
height: 100%;
vertical-align: top;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0 20px;
}
textarea {
border: none;
border-right: 1px solid #ccc;
resize: none;
outline: none;
background-color: #f6f6f6;
font-size: 14px;
font-family: 'Monaco', courier, monospace;
padding: 20px;
}
code {
color: #f66;
}

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue.js modal component example</title>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- template for the modal component -->
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" @click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<!-- app -->
<div id="app">
<button id="show-modal" @click="showModal = true">Show Modal</button>
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
</div>
<script>
// register modal component
Vue.component('modal', {
template: '#modal-template'
})
// start app
new Vue({
el: '#app',
data: {
showModal: false
}
})
</script>
</body>
</html>

View File

@ -0,0 +1,63 @@
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
display: table;
transition: opacity .3s ease;
}
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
.modal-container {
width: 300px;
margin: 0px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
transition: all .3s ease;
font-family: Helvetica, Arial, sans-serif;
}
.modal-header h3 {
margin-top: 0;
color: #42b983;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter {
opacity: 0;
}
.modal-leave-to {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-to .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Move Animations</title>
<style>
.container {
position: relative;
padding: 0;
}
.item {
width: 100%;
height: 30px;
background-color: #f3f3f3;
border: 1px solid #666;
box-sizing: border-box;
}
/* 1. declare transition */
.fade-move, .fade-enter-active, .fade-leave-active {
transition: all .5s cubic-bezier(.55,0,.1,1);
}
/* 2. declare enter from and leave to state */
.fade-enter, .fade-leave-to {
opacity: 0;
transform: scaleY(0.01) translate(30px, 0);
}
/* 3. ensure leaving items are taken out of layout flow so that moving
animations can be calculated correctly. */
.fade-leave-active {
position: absolute;
}
</style>
<script src="https://cdn.jsdelivr.net/lodash/4.3.0/lodash.min.js"></script>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
</head>
<body>
<div id="el">
<button @click="insert">insert at random index</button>
<button @click="reset">reset</button>
<button @click="shuffle">shuffle</button>
<transition-group tag="ul" name="fade" class="container">
<item v-for="item in items"
class="item"
:msg="item"
:key="item"
@rm="remove(item)">
</item>
</transition-group>
</div>
<script>
var items = [1, 2, 3, 4, 5]
var id = items.length + 1
var vm = new Vue({
el: '#el',
data: {
items: items
},
components: {
item: {
props: ['msg'],
template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>`
}
},
methods: {
insert () {
var i = Math.round(Math.random() * this.items.length)
this.items.splice(i, 0, id++)
},
reset () {
this.items = [1, 2, 3, 4, 5]
},
shuffle () {
this.items = _.shuffle(this.items)
},
remove (item) {
var i = this.items.indexOf(item)
if (i > -1) {
this.items.splice(i, 1)
}
}
}
})
</script>
</body>
</html>

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue.js wrapper component example (jquery plugin: select2)</title>
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
<script src="https://unpkg.com/jquery"></script>
<script src="https://unpkg.com/select2@4.0.3"></script>
<link href="https://unpkg.com/select2@4.0.3/dist/css/select2.min.css" rel="stylesheet">
<style>
html, body {
font: 13px/18px sans-serif;
}
select {
min-width: 300px;
}
</style>
</head>
<body>
<div id="el">
</div>
<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
<div>
<p>Selected: {{ selected }}</p>
<select2 :options="options" v-model="selected">
<option disabled value="0">Select one</option>
</select2>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select>
<slot></slot>
</select>
</script>
<script>
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
mounted: function () {
var vm = this
$(this.$el)
.val(this.value)
// init select2
.select2({ data: this.options })
// emit event on change.
.on('change', function () {
vm.$emit('input', this.value)
})
},
watch: {
value: function (value) {
// update value
$(this.$el).val(value).trigger('change')
},
options: function (options) {
// update options
$(this.$el).select2({ data: options })
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
})
var vm = new Vue({
el: '#el',
template: '#demo-template',
data: {
selected: 0,
options: [
{ id: 1, text: 'Hello' },
{ id: 2, text: 'World' }
]
}
})
</script>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue.js SVG graph example</title>
<link rel="stylesheet" href="style.css">
<!-- Delete ".min" for console warnings in development -->
<script src="../../dist/vue.min.js"></script>
<script src="https://unpkg.com/marky/dist/marky.min.js"></script>
</head>
<body>
<!-- template for the polygraph component. -->
<script type="text/x-template" id="polygraph-template">
<g>
<polygon :points="points"></polygon>
<circle cx="100" cy="100" r="80"></circle>
<axis-label
v-for="(stat, index) in stats"
:stat="stat"
:index="index"
:total="stats.length">
</axis-label>
</g>
</script>
<!-- template for the axis label component. -->
<script type="text/x-template" id="axis-label-template">
<text :x="point.x" :y="point.y">{{stat.label}}</text>
</script>
<!-- demo root element -->
<div id="demo">
<!-- Use the component -->
<svg width="200" height="200">
<polygraph :stats="stats"></polygraph>
</svg>
<!-- controls -->
<div v-for="stat in stats">
<label>{{stat.label}}</label>
<input type="range" v-model="stat.value" min="0" max="100">
<span>{{stat.value}}</span>
<button @click="remove(stat)" class="remove">X</button>
</div>
<form id="add">
<input name="newlabel" v-model="newLabel">
<button @click="add">Add a Stat</button>
</form>
<pre id="raw">{{ stats }}</pre>
</div>
<p style="font-size:12px">* input[type="range"] requires IE10 or above.</p>
<script src="svg.js"></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More