diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9c5408b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab + +[{*.json,*.yml,.babelrc,.bowerrc,.browserslistrc,.postcssrc, .eslintrc, .stylelintrc}] +indent_style = space +indent_size = 2 + +[{*.txt,wp-config-sample.php}] +end_of_line = crlf diff --git a/.github/_typos.toml b/.github/_typos.toml index 922b3db..a4e2963 100644 --- a/.github/_typos.toml +++ b/.github/_typos.toml @@ -1,4 +1,20 @@ [default.extend-words] # Add words that should be ignored (define them as themselves) Tung = "Tung" # Example: Don't correct the surname "Tung" -curent = "curent" # Intentionally preserving misspelling to avoid breaking changes \ No newline at end of file +curent = "curent" # Intentionally preserving misspelling to avoid breaking changes +stati = "stati" # Plural for statuses + +[type.php] +extend-ignore-re = [ + # spellchecker:disable-line: + "(?Rm)^.*//\\s*@spellchecker:disable-line(\\b.*)?$", + + # spellchecker:: + "//\\s*@spellchecker:off\\s*\\n.*\\n\\s*//\\s*@spellchecker:on" +] + +[files] +extend-exclude = [ + "build", + "dist" +] diff --git a/.github/blueprints/blueprint.json b/.github/blueprints/blueprint.json new file mode 100644 index 0000000..44e4aa4 --- /dev/null +++ b/.github/blueprints/blueprint.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://playground.wordpress.net/blueprint-schema.json", + "meta": { + "title": "WP Content Connect", + "description": "WordPress library that enables direct relationships for posts to posts and posts to users.", + "author": "10up" + }, + "landingPage": "/wp-admin/", + "preferredVersions": { + "php": "8.3", + "wp": "latest" + }, + "features": { + "networking": true + }, + "login": true, + "steps": [ + { + "step": "installPlugin", + "pluginData": { + "resource": "url", + "url": "https://github.com/10up/wp-content-connect/archive/refs/tags/2.0.0-beta.2.zip" + }, + "options": { + "activate": true, + "targetFolderName": "wp-content-connect" + } + }, + { + "step": "installPlugin", + "pluginData": { + "resource": "git:directory", + "url": "https://github.com/fabiankaegy/wp-content-connect-example-data", + "ref": "main" + }, + "options": { + "activate": true + } + }, + { + "step": "wp-cli", + "command": "wp content-connect-example generate --universities=10 --cities=20 --people=100" + } + ] +} diff --git a/.github/workflows/test-php.yml b/.github/workflows/test-php.yml new file mode 100644 index 0000000..e9111ee --- /dev/null +++ b/.github/workflows/test-php.yml @@ -0,0 +1,55 @@ +name: Test PHP + +env: + COMPOSER_VERSION: "2" + COMPOSER_CACHE: "${{ github.workspace }}/.composer-cache" + +on: + push: + branches: + - develop + - trunk + pull_request: + branches: + - develop + +permissions: + contents: read + +jobs: + test-php: + runs-on: ubuntu-latest + steps: + - name: Check out our repo source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set standard 10up cache directories + run: | + composer config -g cache-dir "${{ env.COMPOSER_CACHE }}" + + - name: Prepare composer cache + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + with: + path: ${{ env.COMPOSER_CACHE }} + key: composer-${{ env.COMPOSER_VERSION }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + composer-${{ env.COMPOSER_VERSION }}- + + - name: Set PHP version + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 + with: + php-version: '8.2' + extensions: :php-psr + coverage: none + + - name: Install Node dependencies + run: npm ci --no-optional + + - name: Install dependencies + run: composer install --ignore-platform-reqs + + - name: Setup Tests + run: npm run test:setup + + - name: Run Tests + run: npm run test:php diff --git a/.gitignore b/.gitignore index 20734ad..02a6955 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.phpunit.result.cache +test-results/ + # Version Control .svn @@ -27,7 +30,3 @@ npm-debug.log sassdoc scss-lint-report.xml config.codekit - -/assets/js/content-connect.js -/assets/js/content-connect.min.js - diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/.wp-env.json b/.wp-env.json new file mode 100644 index 0000000..a2b2d17 --- /dev/null +++ b/.wp-env.json @@ -0,0 +1,15 @@ +{ + "core": "WordPress/WordPress#master", + "phpVersion": "8.2", + "plugins": [ + "." + ], + "mappings": { + "wp-content/plugins/wp-content-connect": "." + }, + "config": { + "WP_DEBUG": true, + "SCRIPT_DEBUG": true + } +} + diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..1673051 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,1162 @@ +# WP Content Connect - Architecture Documentation + +**Version:** 2.0.0 +**Status:** Stable +**UI Framework:** React (WordPress Block Editor components) + +## Introduction + +WP Content Connect is a WordPress library that enables direct relationships between: + +- **Posts to Posts** - Connect any post type to any other post type +- **Posts to Users** - Connect posts to WordPress users + +The plugin provides: + +- Custom database tables for efficient relationship storage +- Integration with `WP_Query` and `WP_User_Query` via `relationship_query` parameter +- Admin UI for managing relationships in both Block Editor and Classic Editor +- REST API V2 for CRUD operations on relationships +- WordPress Data Store for state management +- Helper functions for programmatic access +- JavaScript filter hooks for UI customization + +## Directory Structure + +```txt +wp-content-connect/ +├── .github/ # GitHub workflows +├── assets/ +│ └── js/ +│ ├── index.ts # Block Editor entry point +│ ├── classic-editor.tsx # Classic Editor entry point +│ ├── store/ # WordPress Data Store +│ │ ├── index.ts # Store definition +│ │ ├── types.ts # TypeScript types +│ │ └── api.ts # REST API wrapper functions +│ ├── hooks/ # React hooks +│ │ ├── use-relationships.ts # Relationship data hook +│ │ └── use-related-entities.ts # Related entities hook +│ └── components/ # React components (directory-based) +│ ├── RelationshipManager/ +│ │ └── index.tsx # Content picker UI with filters +│ └── RelationshipsPanel/ +│ └── index.tsx # Block Editor sidebar panel +├── dist/ # Compiled assets (generated) +│ └── js/ +│ ├── block-editor.js # Block Editor bundle +│ ├── block-editor.asset.php # Asset dependencies +│ ├── classic-editor.js # Classic Editor bundle +│ └── classic-editor.asset.php # Asset dependencies +├── includes/ +│ ├── API/ +│ │ ├── V1/ +│ │ │ └── Search.php # Legacy REST API search (deprecated) +│ │ └── V2/ # New REST API +│ │ ├── AbstractRoute.php # Base route class +│ │ ├── Route/ +│ │ │ └── Relationships.php # Global relationships endpoint +│ │ └── Post/Route/ +│ │ ├── AbstractPostRoute.php # Post-specific base class +│ │ ├── Relationships.php # Post relationships endpoint +│ │ └── RelatedEntities.php # Related entities CRUD +│ ├── QueryIntegration/ +│ │ ├── RelationshipQuery.php # WP_Query relationship parser +│ │ ├── UserRelationshipQuery.php # WP_User_Query relationship parser +│ │ ├── UserQueryIntegration.php # WP_User_Query hooks +│ │ └── WPQueryIntegration.php # WP_Query hooks +│ ├── Relationships/ +│ │ ├── DeletedItems.php # Cleanup on post/user deletion +│ │ ├── PostToPost.php # Post-to-post relationship logic +│ │ ├── PostToUser.php # Post-to-user relationship logic +│ │ └── Relationship.php # Abstract base class +│ ├── Tables/ +│ │ ├── BaseTable.php # Abstract table class +│ │ ├── PostToPost.php # Post-to-post table schema +│ │ └── PostToUser.php # Post-to-user table schema +│ ├── UI/ +│ │ ├── BlockEditor.php # Block Editor asset enqueuing +│ │ └── ClassicEditor.php # Classic Editor metaboxes +│ ├── Helpers.php # Helper functions +│ ├── Plugin.php # Main plugin class (singleton) +│ ├── Registry.php # Relationship registry +│ └── REST.php # REST API permission checks +├── tests/ # PHPUnit tests +│ └── php/ +│ ├── Helpers/ # Helper function tests +│ └── API/V2/ # REST API tests +├── autoload.php # PSR-4 autoloader +├── content-connect.php # Main plugin file +├── composer.json # PHP dependencies +└── package.json # JS dependencies (React, WordPress packages) +``` + +## Core Architecture + +### Plugin Initialization Flow + +```txt +content-connect.php + │ + ▼ +Plugin::instance() (singleton) + │ + ├──► Constants: CONTENT_CONNECT_VERSION, CONTENT_CONNECT_URL, CONTENT_CONNECT_PATH + │ + ├──► Table registration + │ └──► admin_init hook → dbDelta() schema updates + │ + ├──► Registry instantiation + │ + ├──► Module setup (all implement setup() method) + │ ├──► WPQueryIntegration (posts_where, posts_join, etc.) + │ ├──► UserQueryIntegration (pre_user_query) + │ ├──► ClassicEditor (add_meta_boxes, admin_enqueue_scripts) + │ ├──► BlockEditor (enqueue_block_editor_assets) + │ ├──► DeletedItems (deleted_post, deleted_user hooks) + │ ├──► REST (rest_api_init for permission checks) + │ ├──► API\V1\Search (legacy search endpoint) + │ ├──► API\V2\Route\Relationships + │ ├──► API\V2\Post\Route\Relationships + │ └──► API\V2\Post\Route\RelatedEntities + │ + └──► do_action('tenup-content-connect-init') @ priority 100 + └──► Developers define relationships here +``` + +### Component Responsibilities + +| Component | Responsibility | +|-----------|---------------| +| `Plugin` | Bootstrap, dependency injection, module orchestration | +| `Registry` | Stores and retrieves relationship definitions | +| `Relationships/*` | Business logic for adding/removing/querying relationships | +| `Tables/*` | Database schema, CRUD operations | +| `QueryIntegration/*` | Hooks into WP_Query and WP_User_Query | +| `UI/BlockEditor` | Enqueues Block Editor React components | +| `UI/ClassicEditor` | Renders metaboxes for Classic Editor | +| `API/V1/Search` | Legacy REST endpoint (deprecated) | +| `API/V2/*` | Modern REST API with full CRUD | +| `Helpers` | Utility functions for programmatic access | + +## Database Schema + +### Table: `{prefix}post_to_post` + +Stores post-to-post relationships with **bidirectional storage** (both directions are stored). + +```sql +CREATE TABLE {prefix}post_to_post ( + id1 bigint(20) unsigned NOT NULL, + id2 bigint(20) unsigned NOT NULL, + name varchar(64) NOT NULL, + order int(11) NOT NULL default 0, + UNIQUE KEY id1_id2_name (id1, id2, name), + KEY id2_name (id2, name), + KEY id2_name_order (id2, name, order) +); +``` + +**Schema Version:** 0.1.10 + +**Columns:** + +- `id1` - Source post ID +- `id2` - Target post ID +- `name` - Relationship name identifier +- `order` - Sort order of `id2` when viewed from `id1` + +**Storage Strategy:** +When connecting Post A to Post B, two rows are created: + +1. `id1=A, id2=B, name=rel, order=X` (A's view of B) +2. `id1=B, id2=A, name=rel, order=Y` (B's view of A) + +### Table: `{prefix}post_to_user` + +Stores post-to-user relationships with **unidirectional storage** but separate order columns. + +```sql +CREATE TABLE {prefix}post_to_user ( + post_id bigint(20) unsigned NOT NULL, + user_id bigint(20) unsigned NOT NULL, + name varchar(64) NOT NULL, + user_order int(11) NOT NULL default 0, + post_order int(11) NOT NULL default 0, + UNIQUE KEY post_user_name (post_id, user_id, name), + KEY user_name (user_id, name), + KEY user_name_order (user_id, name, post_order), + KEY post_name (post_id, name), + KEY post_name_order (post_id, name, user_order) +); +``` + +**Schema Version:** 0.1.10 + +**Columns:** + +- `post_id` - Post ID +- `user_id` - User ID +- `name` - Relationship name identifier +- `user_order` - Order of users when viewing from a post +- `post_order` - Order of posts when viewing from a user + +### Index Strategy + +Indexes are designed for common query patterns: + +- `id1_id2_name` / `post_user_name` - Prevents duplicates, fast lookups +- `id2_name` / `user_name` / `post_name` - Queries without ordering +- `id2_name_order` / `*_order` - Queries with ORDER BY relationship + +## PHP Class Reference + +### Plugin.php + +Main plugin class using singleton pattern. + +```php +namespace TenUp\ContentConnect; + +class Plugin { + public $tables = []; // Table instances ('p2p', 'p2u') + public $registry; // Registry instance + + public static function instance(); // Get singleton instance + public function get_registry(); // Get Registry instance + public function get_table($table_name); // Get table instance ('p2p' or 'p2u') +} +``` + +**Constants Defined:** + +- `CONTENT_CONNECT_VERSION` - Plugin version +- `CONTENT_CONNECT_URL` - Plugin URL +- `CONTENT_CONNECT_PATH` - Plugin filesystem path + +**Key Hooks:** + +- `init` @ priority 100 - Fires `tenup-content-connect-init` +- `admin_init` - Runs table schema upgrades + +### Registry.php + +Central registry for all relationship definitions. + +```php +namespace TenUp\ContentConnect; + +class Registry { + // Define a post-to-post relationship + public function define_post_to_post($from, $to, $name, $args = []); + + // Define a post-to-user relationship + public function define_post_to_user($post_type, $name, $args = []); + + // Retrieve relationships + public function get_post_to_post_relationship($from, $to, $name); + public function get_post_to_user_relationship($post_type, $name); + public function get_post_to_post_relationship_by_key($key); + public function get_post_to_user_relationship_by_key($key); + + // Get all relationships + public function get_post_to_post_relationships(); + public function get_post_to_user_relationships(); +} +``` + +**Relationship Key Format:** `{from}_{to}_{name}` + +**Arguments ($args):** + +- `enable_from_ui` (bool) - Show UI on "from" post type edit screen +- `enable_to_ui` (bool) - Show UI on "to" post type edit screen +- `from_labels` (array) - Labels for "from" side UI +- `to_labels` (array) - Labels for "to" side UI +- `from_sortable` (bool) - Enable drag-and-drop on "from" side +- `to_sortable` (bool) - Enable drag-and-drop on "to" side +- `from_max_items` (int) - Maximum items on "from" side (0 = unlimited) +- `to_max_items` (int) - Maximum items on "to" side (0 = unlimited) + +### Helpers.php + +Helper functions for programmatic access to relationship data. + +```php +namespace TenUp\ContentConnect\Helpers; + +// Get Plugin singleton +function get_plugin(); + +// Get Registry instance +function get_registry(); + +// Get related IDs by relationship name +function get_related_ids_by_name($post_id, $relationship_name); + +// Query post-to-post relationships by field +// $field: 'any', 'key', 'post_type', 'from', 'to' +function get_post_to_post_relationships_by($field = 'any', $value = ''); + +// Query post-to-user relationships by field +// $field: 'any', 'key', 'post_type' +function get_post_to_user_relationships_by($field = 'any', $value = ''); + +// Get relationship data for a post +// $rel_type: 'any', 'post-to-post', 'post-to-user' +// $context: 'view' (metadata only) or 'embed' (includes related entities) +function get_post_relationships_data($post, $rel_type = 'any', $other_post_type = false, $context = 'view'); + +// Get post-to-post relationship data +function get_post_to_post_relationships_data($post, $other_post_type = false, $context = 'view'); + +// Get post-to-user relationship data +function get_post_to_user_relationships_data($post, $context = 'view'); +``` + +### Relationships/Relationship.php (Abstract) + +Base class for relationship types. + +```php +namespace TenUp\ContentConnect\Relationships; + +abstract class Relationship { + public $name; + public $id; + public $enable_from_ui; + public $enable_to_ui; + public $from_labels; + public $to_labels; + public $from_sortable; + public $to_sortable; + public $from_max_items; + public $to_max_items; +} +``` + +### Relationships/PostToPost.php + +Post-to-post relationship implementation. + +```php +namespace TenUp\ContentConnect\Relationships; + +class PostToPost extends Relationship { + public $from; // Post type + public $to; // Post type (can be array) + + // Get related post IDs + public function get_related_object_ids($post_id, $order_by_relationship = false); + + // Add relationship (stores both directions) + public function add_relationship($pid1, $pid2); + + // Delete relationship (removes both directions) + public function delete_relationship($pid1, $pid2); + + // Replace all relationships for a post + public function replace_relationships($post_id, $related_ids); + + // Save sort order + public function save_sort_data($object_id, $ordered_ids); +} +``` + +### Relationships/PostToUser.php + +Post-to-user relationship implementation. + +```php +namespace TenUp\ContentConnect\Relationships; + +class PostToUser extends Relationship { + public $post_type; + + // Get related IDs + public function get_related_post_ids($user_id, $order_by_relationship = false); + public function get_related_user_ids($post_id, $order_by_relationship = false); + + // Add/delete relationships + public function add_relationship($post_id, $user_id); + public function delete_relationship($post_id, $user_id); + + // Replace operations + public function replace_post_to_user_relationships($post_id, $user_ids); + public function replace_user_to_post_relationships($user_id, $post_ids); + + // Sort operations + public function save_post_to_user_sort_data($post_id, $ordered_user_ids); + public function save_user_to_post_sort_data($user_id, $ordered_post_ids); +} +``` + +### Tables/BaseTable.php (Abstract) + +Base class for custom database tables. + +```php +namespace TenUp\ContentConnect\Tables; + +abstract class BaseTable { + public $db_version; // Schema version string + public $table_name; // Table name without prefix + + public function get_table_name(); // Get full table name with prefix + abstract public function get_schema(); // Get current schema SQL + public function maybe_upgrade_table(); // Runs dbDelta if version changed + public function replace($data); // INSERT ... ON DUPLICATE KEY UPDATE + public function replace_bulk($rows); // Bulk replace + public function delete($where); // DELETE with WHERE +} +``` + +### UI/BlockEditor.php + +Enqueues Block Editor assets (JavaScript and CSS). + +```php +namespace TenUp\ContentConnect\UI; + +class BlockEditor { + public function setup(); // Hooks enqueue_block_editor_assets + public function enqueue_block_editor_assets(); // Enqueues block-editor.js and admin-styles.css +} +``` + +**Asset Loading:** + +- Loads `dist/js/block-editor.js` with dependencies from `.asset.php` +- Loads `dist/css/admin-styles.css` if the asset file exists +- Both assets check for file existence before enqueuing + +### UI/ClassicEditor.php + +Manages Classic Editor metaboxes with React components. + +```php +namespace TenUp\ContentConnect\UI; + +class ClassicEditor { + public function setup(); // Hooks admin_enqueue_scripts, add_meta_boxes + public function enqueue_classic_editor_assets($hook_suffix); + public function add_relationships_meta_boxes($post_type, $post); + public function render_relationship_meta_box($post, $args); +} +``` + +**Metabox Container:** +Renders a `
` with data attributes for React to mount: + +- `data-content-connect` - Marker attribute +- `data-post-id` - Current post ID +- `data-relationship` - JSON-encoded relationship data + +## REST API + +### V2 Endpoints + +| Method | Endpoint | Purpose | +|--------|----------|---------| +| GET | `/content-connect/v2/relationships` | List all relationship definitions | +| GET | `/content-connect/v2/post/{id}/relationships` | Get relationships for a post | +| GET | `/content-connect/v2/post/{id}/related` | List related entities | +| POST | `/content-connect/v2/post/{id}/related` | Replace all relationships | +| PUT | `/content-connect/v2/post/{id}/related` | Add single relationship | +| DELETE | `/content-connect/v2/post/{id}/related` | Remove single relationship | + +### API\V2\AbstractRoute.php + +Base class for V2 REST routes. + +```php +namespace TenUp\ContentConnect\API\V2; + +abstract class AbstractRoute { + protected $namespace = 'content-connect/v2'; + + abstract public function setup(); + abstract public function register_routes(); + + protected function get_post($post_id); // Validates and returns WP_Post + protected function get_user($user_id); // Validates and returns WP_User +} +``` + +### API\V2\Route\Relationships.php + +Global relationships discovery endpoint. + +**Endpoint:** `GET /content-connect/v2/relationships` + +**Parameters:** + +- `rel_type` (string) - Filter by type: 'post-to-post' or 'post-to-user' +- `filter_by` (string) - Field to filter: 'post_type', 'from', 'to' +- `filter_value` (string) - Value to match + +### API\V2\Post\Route\Relationships.php + +Post-specific relationships endpoint. + +**Endpoint:** `GET /content-connect/v2/post/{id}/relationships` + +**Parameters:** + +- `id` (int, required) - Post ID +- `rel_type` (string) - Filter by relationship type +- `post_type` (string) - Filter by related post type +- `context` (string) - 'view' or 'embed' + +### API\V2\Post\Route\RelatedEntities.php + +CRUD operations for related entities. + +**Endpoints:** + +``` +GET /content-connect/v2/post/{id}/related?rel_key=X&rel_type=Y +POST /content-connect/v2/post/{id}/related (body: {rel_key, rel_type, related_ids[]}) +PUT /content-connect/v2/post/{id}/related (body: {rel_key, rel_type, related_id}) +DELETE /content-connect/v2/post/{id}/related (body: {rel_key, rel_type, related_id}) +``` + +**Response Format:** + +```json +{ + "ID": 123, + "id": 123, + "name": "Post Title", + "type": "post", + "uuid": "abc123..." +} +``` + +**Pagination Headers:** + +- `X-WP-Total` - Total items +- `X-WP-TotalPages` - Total pages + +### V1 Legacy Endpoint (Deprecated) + +> **Deprecated since 2.0.0:** Use REST API V2 endpoints instead. + +**Endpoint:** `POST /content-connect/v1/search` + +**Parameters:** + +- `nonce` - Security nonce +- `object_type` - 'post' or 'user' +- `post_type` - Post type(s) to search +- `search` - Search term +- `paged` - Page number +- `relationship_name` - Relationship identifier +- `current_post_id` - Current post being edited + +## JavaScript Architecture + +### Technology Stack + +- React (via WordPress packages) +- TypeScript +- WordPress Data API (@wordpress/data) +- @10up/block-components (ContentPicker) +- 10up-toolkit (build system) + +### Build Configuration + +```json +{ + "10up-toolkit": { + "entry": { + "block-editor": "assets/js/index.ts", + "classic-editor": "assets/js/classic-editor.tsx" + } + } +} +``` + +### WordPress Data Store + +**Store Name:** `wp-content-connect` + +#### State Shape + +```typescript +type ContentConnectState = { + relationships: { + [postId: number]: ContentConnectRelationships; + }; + relatedEntities: { + [key: string]: ContentConnectRelatedEntities; + }; + dirtyEntityIds: Set; +}; +``` + +#### Selectors + +| Selector | Parameters | Returns | +|----------|------------|---------| +| `getRelationships` | `postId, options?` | Relationship definitions | +| `getRelatedEntities` | `postId, options` | Array of related entities | +| `getDirtyEntityIds` | - | Array of post IDs with unsaved changes | + +#### Actions + +| Action | Parameters | Purpose | +|--------|------------|---------| +| `setRelationships` | `postId, relationships` | Set relationship definitions | +| `setRelatedEntities` | `key, entities` | Set related entities for a key | +| `updateRelatedEntities` | `postId, relKey, relType, entities` | Update and mark as dirty | +| `markPostAsDirty` | `postId` | Mark post as having unsaved changes | +| `clearDirtyEntities` | - | Clear all dirty flags | + +#### Resolvers + +Resolvers automatically fetch data from REST API when selectors are called: + +```typescript +// When getRelationships is called, if data not in store: +GET /content-connect/v2/post/{postId}/relationships + +// When getRelatedEntities is called, if data not in store: +// Uses getAllRelatedEntities() to fetch all pages automatically +GET /content-connect/v2/post/{postId}/related?rel_key=X&rel_type=Y&per_page=100&page=1 +// Continues fetching until all pages retrieved via X-WP-TotalPages header +``` + +#### API Wrapper Functions + +The store uses wrapper functions in `store/api.ts`: + +| Function | Purpose | +|----------|---------| +| `getRelationships` | Fetch relationship definitions for a post | +| `getRelatedEntities` | Fetch single page of related entities | +| `getAllRelatedEntities` | Fetch all pages of related entities (pagination) | +| `updateRelatedEntities` | Replace all related entities for a relationship | + +**Pagination Support:** + +The `getAllRelatedEntities()` function handles large relationship lists by: + +1. Fetching with `per_page=100` (configurable via options) +2. Reading `X-WP-TotalPages` response header +3. Looping through all pages until complete +4. Returning concatenated results + +```typescript +export async function getAllRelatedEntities( + postId: number, + options: GetRelatedEntitiesOptions +): Promise { + const perPage = options.per_page ?? 100; + // Fetches all pages and returns combined array +} +``` + +#### Auto-Persistence + +The store registers a filter on `editor.preSavePost` that: + +1. Checks for dirty entity IDs +2. For each dirty post, iterates relationships +3. Calls REST API to persist changes +4. Clears dirty flags on success + +### React Hooks + +#### useRelationships + +```typescript +function useRelationships( + postId: number, + options?: GetRelationshipsOptions +): [hasResolved: boolean, relationships: ContentConnectRelationships] +``` + +#### useRelatedEntities + +```typescript +function useRelatedEntities( + postId: number, + options: GetRelatedEntitiesOptions +): [ + hasResolved: boolean, + entities: ContentConnectRelatedEntities, + updateFn: (entities: ContentConnectRelatedEntities) => void +] +``` + +### React Components + +#### RelationshipsPanel (Block Editor) + +**Location:** `assets/js/components/RelationshipsPanel/index.tsx` + +Registered as a Gutenberg plugin that renders Document Settings panels. + +```tsx +// Renders one panel per relationship with enable_ui: true + + + +``` + +#### RelationshipManager + +**Location:** `assets/js/components/RelationshipManager/index.tsx` + +Wrapper around `@10up/block-components` ContentPicker with full TypeScript support. + +**Props:** + +- `postId` - Current post ID (number | null) +- `relationship` - Relationship definition object (ContentConnectRelationship) + +**Features:** + +- Configures ContentPicker based on relationship settings +- Handles post-to-post and post-to-user modes +- Supports sortable (drag-and-drop) +- Applies JavaScript filter hooks via `useMemo` for performance +- Full TypeScript types for filter callbacks + +**TypeScript Types:** + +```typescript +type SearchResultFilter = ( + item: NormalizedSuggestion, + originalResult: WP_REST_API_Search_Result | WP_REST_API_User +) => NormalizedSuggestion; + +type PickedItemFilter = ( + item: Partial, + originalResult: Post | Term | User +) => Partial; + +type FilterContext = { + rel_key: string; + rel_type: string; + postId: number | null; + mode: 'post' | 'user' | 'term'; +}; +``` + +**CSS Classes:** + +- `content-connect-relationship-manager` +- `content-connect-relationship-manager-{rel_name}` +- `content-connect-relationship-manager-{rel_key}` + +### JavaScript Filter Hooks + +Filters for customizing the UI behavior: + +| Filter | Signature | Purpose | +|--------|-----------|---------| +| `contentConnect.searchResultFilter` | `(defaultFilter, context) => filterFn` | Customize search results | +| `contentConnect.pickedItemFilter` | `(defaultFilter, context) => filterFn` | Customize picked items | +| `contentConnect.pickedItemPreviewComponent` | `(Component, context) => Component` | Replace preview component | + +**Context Object:** + +```typescript +{ + rel_key: string; // Relationship key + rel_type: string; // 'post-to-post' or 'post-to-user' + postId: number; // Current post ID + mode: string; // 'post' | 'user' | 'term' +} +``` + +**Example:** + +```javascript +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'contentConnect.searchResultFilter', + 'my-plugin/add-post-id', + (defaultFilter, context) => { + return (item, result) => ({ + ...item, + info: `ID: ${result.id}`, + }); + } +); +``` + +### Classic Editor Integration + +The Classic Editor entry point (`classic-editor.tsx`): + +1. Finds all `[data-content-connect]` containers on `domReady` +2. Parses relationship data from `data-relationship` JSON attribute +3. Mounts `RelationshipManager` components into each container +4. Initializes relationships in the store via `setRelationships()` +5. Intercepts form submission to call exported `persistContentConnectChanges()` +6. Re-submits form after persistence completes + +**Store Integration:** + +The classic editor imports the shared store and `persistContentConnectChanges`: + +```typescript +import { store, persistContentConnectChanges } from './store'; +``` + +The `markPostAsDirty` action gracefully handles the missing editor store in classic editor context by catching errors when attempting to call `editPost()`. + +## Query Integration + +### WP_Query relationship_query + +Query posts by their relationships using the `relationship_query` parameter. + +**Basic Usage:** + +```php +$query = new WP_Query([ + 'post_type' => 'post', + 'relationship_query' => [ + [ + 'name' => 'related_articles', + 'related_to_post' => 123, + ], + ], +]); +``` + +**Multiple Conditions (AND):** + +```php +$query = new WP_Query([ + 'post_type' => 'post', + 'relationship_query' => [ + 'relation' => 'AND', + [ + 'name' => 'related_articles', + 'related_to_post' => 123, + ], + [ + 'name' => 'featured_in', + 'related_to_post' => 456, + ], + ], +]); +``` + +**Order by Relationship:** + +```php +$query = new WP_Query([ + 'post_type' => 'post', + 'orderby' => 'relationship', + 'relationship_query' => [ + [ + 'name' => 'related_articles', + 'related_to_post' => 123, + ], + ], +]); +``` + +**Related to User:** + +```php +$query = new WP_Query([ + 'post_type' => 'post', + 'relationship_query' => [ + [ + 'name' => 'authored_by', + 'related_to_user' => 1, + ], + ], +]); +``` + +### WP_User_Query relationship_query + +Query users by their relationships. + +```php +$query = new WP_User_Query([ + 'relationship_query' => [ + [ + 'name' => 'editors', + 'related_to_post' => 123, + ], + ], +]); +``` + +## Developer API + +### Defining Relationships + +Hook into `tenup-content-connect-init` to define relationships: + +```php +add_action('tenup-content-connect-init', function() { + $registry = \TenUp\ContentConnect\Helpers\get_registry(); + + // Post-to-Post relationship + $registry->define_post_to_post( + 'post', // From post type + 'post', // To post type (can be array) + 'related', // Relationship name + [ + 'enable_from_ui' => true, + 'enable_to_ui' => true, + 'from_labels' => [ + 'name' => 'Related Posts', + 'singular' => 'Related Post', + ], + 'to_labels' => [ + 'name' => 'Related Posts', + 'singular' => 'Related Post', + ], + 'from_sortable' => true, + 'to_sortable' => true, + 'from_max_items' => 0, // 0 = unlimited + 'to_max_items' => 5, + ] + ); + + // Post-to-User relationship + $registry->define_post_to_user( + 'post', // Post type + 'authors', // Relationship name + [ + 'enable_from_ui' => true, + 'from_labels' => [ + 'name' => 'Authors', + 'singular' => 'Author', + ], + 'from_sortable' => true, + ] + ); +}); +``` + +### Programmatic Relationship Management + +```php +use function TenUp\ContentConnect\Helpers\get_registry; + +$registry = get_registry(); + +// Get relationship object +$relationship = $registry->get_post_to_post_relationship('post', 'post', 'related'); + +// Add a relationship +$relationship->add_relationship($post_id_1, $post_id_2); + +// Remove a relationship +$relationship->delete_relationship($post_id_1, $post_id_2); + +// Get related IDs +$related_ids = $relationship->get_related_object_ids($post_id); + +// Replace all relationships +$relationship->replace_relationships($post_id, [10, 20, 30]); + +// Save sort order +$relationship->save_sort_data($post_id, [30, 10, 20]); +``` + +### Using Helper Functions + +```php +use function TenUp\ContentConnect\Helpers\get_post_relationships_data; +use function TenUp\ContentConnect\Helpers\get_post_to_post_relationships_by; + +// Get all relationships for a post (metadata only) +$relationships = get_post_relationships_data($post_id); + +// Get relationships with related entities included +$relationships = get_post_relationships_data($post_id, 'any', false, 'embed'); + +// Get all post-to-post relationships for a post type +$relationships = get_post_to_post_relationships_by('post_type', 'post'); +``` + +### Available PHP Filters + +| Filter | Description | +|--------|-------------| +| `tenup_content_connect_post_relationship_data` | Modify relationship data | +| `tenup_content_connect_post_ui_query_args` | Filter post query args | +| `tenup_content_connect_post_ui_user_query_args` | Filter user query args | +| `tenup_content_connect_search_posts_query_args` | Filter search query for posts | +| `tenup_content_connect_search_users_query_args` | Filter search query for users | +| `tenup_content_connect_final_post` | Modify post data before output | +| `tenup_content_connect_final_user` | Modify user data before output | +| `tenup_content_connect_post_item_data` | Filter post item in REST/helpers | +| `tenup_content_connect_user_item_data` | Filter user item in REST/helpers | + +### Available PHP Actions + +| Action | Description | +|--------|-------------| +| `tenup-content-connect-init` | Define relationships (fires at priority 100 on init) | + +### Available JavaScript Filters + +| Filter | Description | +|--------|-------------| +| `contentConnect.searchResultFilter` | Customize search result display | +| `contentConnect.pickedItemFilter` | Customize picked item display | +| `contentConnect.pickedItemPreviewComponent` | Replace preview component | + +## Security Considerations + +### REST API Authentication + +All V2 endpoints require: + +- Authenticated user +- `edit_post` capability for the target post + +```php +'permission_callback' => function($request) { + $post_id = $request->get_param('id'); + return current_user_can('edit_post', $post_id); +} +``` + +### SQL Escaping + +All database operations use `$wpdb->prepare()` for parameterized queries: + +```php +$wpdb->prepare( + "SELECT id2 FROM {$table} WHERE id1 = %d AND name = %s", + $post_id, + $this->name +); +``` + +## Build System + +### PHP Dependencies (Composer) + +```json +{ + "require": { + "php": ">=7.4", + "composer/installers": "^2.3" + }, + "require-dev": { + "10up/phpcs-composer": "^3.0", + "phpcompatibility/php-compatibility": "dev-develop as 9.99.99", + "phpunit/phpunit": "^9.0", + "yoast/phpunit-polyfills": "^4.0" + }, + "autoload": { + "psr-4": { + "TenUp\\ContentConnect\\": "includes", + "TenUp\\ContentConnect\\Tests\\": "tests/php" + }, + "files": [ + "includes/Helpers.php" + ] + } +} +``` + +**Note:** Helper functions are loaded via Composer's `files` autoload to ensure they are available globally. + +### JavaScript Dependencies (npm) + +```json +{ + "dependencies": { + "@10up/block-components": "^1.22.1", + "10up-toolkit": "^6.5.1" + }, + "devDependencies": { + "@wordpress/api-fetch": "^7.18.0", + "@wordpress/data": "^10.18.0", + "@wordpress/edit-post": "^8.18.0", + "@wordpress/editor": "^14.18.0", + "@wordpress/env": "^10.39.0", + "@wordpress/html-entities": "^4.18.0", + "@wordpress/url": "^4.18.0", + "core-js": "^3.38.0", + "node-wp-i18n": "^1.2.7" + } +} +``` + +### Build Commands + +```bash +# Development (watch mode) +npm run start + +# Production build +npm run build + +# Lint JavaScript +npm run lint-js + +# Format JavaScript +npm run format-js + +# Run PHPUnit tests +npm run test:unit +``` + +Build outputs: + +- `dist/js/block-editor.js` +- `dist/js/block-editor.asset.php` +- `dist/js/classic-editor.js` +- `dist/js/classic-editor.asset.php` + +## Testing + +### PHPUnit Tests + +```bash +# Start wp-env +npm run wp-env:start + +# Run tests +npm run test:unit + +# Or directly +./vendor/bin/phpunit -c phpunit.xml +``` + +### Test Structure + +``` +tests/php/ +├── Helpers/ # Helper function tests +│ ├── GetPluginTest.php +│ ├── GetRegistryTest.php +│ ├── GetRelatedIdsByNameTest.php +│ ├── GetPostToPostRelationshipsByTest.php +│ ├── GetPostToUserRelationshipsByTest.php +│ ├── GetPostRelationshipsDataTest.php +│ ├── GetPostToPostRelationshipsDataTest.php +│ └── GetPostToUserRelationshipsDataTest.php +└── API/V2/ # REST API tests + ├── RelationshipsRouteTest.php + ├── PostRelationshipsRouteTest.php + └── RelatedEntitiesRouteTest.php +``` + +## Version History + +- **2.0.0** - React migration, REST API V2, WordPress Data Store +- **1.6.0** - Previous version (Vue.js UI) +- Schema version: 0.1.10 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9856b36..ce0136b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to this project will be documented in this file, per [the Ke ## [Unreleased] - TBD +## [2.0.0] - TBD +### Added +- New helper functions for programmatic access to relationship data: `get_post_to_post_relationships_by()`, `get_post_to_user_relationships_by()`, `get_post_relationships_data()`, `get_post_to_post_relationships_data()`, `get_post_to_user_relationships_data()` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#96](https://github.com/10up/wp-content-connect/pull/96)). +- New V2 REST API endpoints for managing relationships: `GET/POST/PUT/DELETE /content-connect/v2/post/{id}/related`, `GET /content-connect/v2/post/{id}/relationships`, `GET /content-connect/v2/relationships` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#95](https://github.com/10up/wp-content-connect/pull/95)). +- WordPress data API store (`wp-content-connect`) for state management in the Block Editor (props [@fabiankaegy](https://github.com/fabiankaegy) [@s3rgiosan](https://github.com/s3rgiosan) via [#94](https://github.com/10up/wp-content-connect/pull/94)). +- React hooks for accessing relationship data: `useRelationships()`, `useRelatedEntities()` (props [@fabiankaegy](https://github.com/fabiankaegy) [@s3rgiosan](https://github.com/s3rgiosan) via [#94](https://github.com/10up/wp-content-connect/pull/94)). +- Block Editor integration (props [@fabiankaegy](https://github.com/fabiankaegy) [@s3rgiosan](https://github.com/s3rgiosan) via [#94](https://github.com/10up/wp-content-connect/pull/94)). +- JavaScript filter hooks for customizing the UI: `contentConnect.searchResultFilter`, `contentConnect.pickedItemFilter`, `contentConnect.pickedItemPreviewComponent` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#104](https://github.com/10up/wp-content-connect/pull/104)). +- Integration tests for all new helper functions and REST API endpoints (props [@s3rgiosan](https://github.com/s3rgiosan)). + +### Changed +- Migrated Classic Editor UI from Vue.js to React, using the same components as Block Editor (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). +- Replaced Vue.js MetaBox with new ClassicEditor and BlockEditor PHP classes (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). +- Build system updated from Browserify/Vueify to 10up-toolkit with TypeScript support (props [@fabiankaegy](https://github.com/fabiankaegy) [@s3rgiosan](https://github.com/s3rgiosan) via [#94](https://github.com/10up/wp-content-connect/pull/94)). +- JavaScript bundle now split into `block-editor.js` and `classic-editor.js` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). + +### Removed +- Vue.js dependencies: `vue`, `vue-resource`, `vuedraggable` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). +- Browserify build dependencies: `browserify`, `vueify`, `watchify` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). +- Vue.js UI classes: `MetaBox`, `PostUI`, `PostToPost`, `PostToUser` from `includes/UI/` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). +- Vue.js source files from `assets/js/src/` (props [@s3rgiosan](https://github.com/s3rgiosan) via [#97](https://github.com/10up/wp-content-connect/pull/97)). + +### Deprecated +- REST API V1 Search endpoint (`/content-connect/v1/search`) - use V2 endpoints instead (props [@s3rgiosan](https://github.com/s3rgiosan) via [#95](https://github.com/10up/wp-content-connect/pull/95)). + ## [1.6.0] - 2025-02-14 ### Added - Filters for the Post UI `WP_Query` and `WP_User_Query` args (props [@s3rgiosan](https://github.com/s3rgiosan), [@rickalee](https://github.com/rickalee) via [#67](https://github.com/10up/wp-content-connect/pull/67)). @@ -69,6 +94,7 @@ All notable changes to this project will be documented in this file, per [the Ke - Initial plugin release. [Unreleased]: https://github.com/10up/wp-content-connect/compare/master...develop +[2.0.0]: https://github.com/10up/wp-content-connect/compare/1.6.0...2.0.0 [1.6.0]: https://github.com/10up/wp-content-connect/compare/1.5.0...1.6.0 [1.5.0]: https://github.com/10up/wp-content-connect/compare/1.4.0...1.5.0 [1.4.0]: https://github.com/10up/wp-content-connect/compare/1.3.0...1.4.0 diff --git a/README.md b/README.md index 884e4e7..8d4aad1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # WP Content Connect -> WordPress library that enables direct relationships for posts to posts and posts to users. +[![Support Level](https://img.shields.io/badge/support-stable-blue.svg)](#support-level) ![WordPress tested up to version](https://img.shields.io/badge/WordPress-v6.7%20tested-success.svg) [![CodeQL](https://github.com/10up/wp-content-connect/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/10up/wp-content-connect/actions/workflows/github-code-scanning/codeql) [![GPL-3.0-or-later License](https://img.shields.io/github/license/10up/wp-content-connect.svg)](https://github.com/10up/wp-content-connect/blob/master/LICENSE.md) [![WordPress Playground Demo](https://img.shields.io/badge/Playground_Demo-8A2BE2?logo=wordpress&logoColor=FFFFFF&labelColor=3858E9&color=3858E9)](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/wp-content-connect/refs/heads/release/1.7.0/.github/blueprints/blueprint.json) -[![Support Level](https://img.shields.io/badge/support-stable-blue.svg)](#support-level) ![WordPress tested up to version](https://img.shields.io/badge/WordPress-v6.7%20tested-success.svg) [![CodeQL](https://github.com/10up/wp-content-connect/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/10up/wp-content-connect/actions/workflows/github-code-scanning/codeql) [![GPL-3.0-or-later License](https://img.shields.io/github/license/10up/wp-content-connect.svg)](https://github.com/10up/wp-content-connect/blob/master/LICENSE.md) +> WordPress library that enables direct relationships for posts to posts and posts to users. ## Installation and Usage @@ -18,7 +18,7 @@ First, require this repository using the command line: or directly in `composer.json`: -``` +```json "require": { "10up/wp-content-connect": "^1.6.0" } @@ -30,7 +30,7 @@ This will install WP Content Connect to your `vendor` folder and allow you to to Alternatively, if you prefer to have composer install it as a plugin, you may redeclare this package in your `composer.json` using the following example: -``` +```json { "name": "your project name", "repositories": [ @@ -229,6 +229,7 @@ For example, this is fine: ``` while this will not work (orderby will be ignored): + ```php 'relationship_query' => array( array( @@ -236,9 +237,9 @@ while this will not work (orderby will be ignored): 'name' => 'related', ), array( - 'related_to_post' => 15, - 'name' => 'related', - ), + 'related_to_post' => 15, + 'name' => 'related', + ), ), 'orderby' => 'relationship', ``` @@ -474,6 +475,240 @@ User ID 1 has 5 posts that need to be stored in the following order: 4, 2, 7, 9, $relationship->save_user_to_post_sort_data( 1, array( 4, 2, 7, 9, 8 ) ); ``` +## Customizing the Block Editor UI + +Content Connect provides WordPress JavaScript filters that allow you to customize the search results and picked items display in the Block Editor. These filters are context-aware and receive relationship information, enabling both global and per-relationship customization. + +### Available Filters + +#### `contentConnect.searchResultFilter` + +Customizes how search results are displayed in the ContentPicker component. This filter receives the default filter function and a context object containing relationship information. + +**Filter:** + +```javascript +addFilter( + 'contentConnect.searchResultFilter', + 'your-plugin/namespace', + (defaultFilter, context) => { + // Return a custom filter function + } +); +``` + +**Context Object:** + +```typescript +{ + rel_key: string; // The relationship key + rel_type: string; // 'post-to-post' or 'post-to-user' + postId: number | null; // Current post ID + mode: 'post' | 'user'; // Content search mode +} +``` + +#### `contentConnect.pickedItemFilter` + +Customizes how picked items are displayed in the ContentPicker component list. This filter receives the default filter function and a context object containing relationship information. + +**Filter:** + +```javascript +addFilter( + 'contentConnect.pickedItemFilter', + 'your-plugin/namespace', + (defaultFilter, context) => { + // Return a custom filter function + } +); +``` + +#### `contentConnect.pickedItemPreviewComponent` + +Customizes the React component used to render picked items in the ContentPicker component list. This filter receives the default filter and a context object containing relationship information. + +**Filter:** + +```javascript +addFilter( + 'contentConnect.pickedItemPreviewComponent', + 'your-plugin/namespace', + (defaultComponent, context) => { + // Return a custom React component + } +); +``` + +### Usage Examples + +#### Global Customization + +Apply the same customization to all relationships: + +```javascript +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'contentConnect.searchResultFilter', + 'my-project/customize-search-results', + (defaultFilter, context) => { + return (item, result) => { + return { + ...item, + url: '', + info: `ID: ${result.id}`, + }; + }; + } +); + +addFilter( + 'contentConnect.pickedItemFilter', + 'my-project/customize-picked-items', + (defaultFilter, context) => { + return (item, result) => { + return { + ...item, + url: '', + info: `ID: ${result.id}`, + }; + }; + } +); +``` + +#### Per-Relationship Customization + +Customize behavior based on the relationship key or type: + +```javascript +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'contentConnect.searchResultFilter', + 'my-project/customize-specific-relationship', + (defaultFilter, context) => { + // Only customize for a specific relationship + if (context.rel_key === 'my-specific-relationship') { + return (item, result) => { + return { + ...item, + url: '', + info: `Special: ${result.id}`, + }; + }; + } + // Return default for other relationships + return defaultFilter; + } +); + +addFilter( + 'contentConnect.pickedItemFilter', + 'my-project/customize-picked-items-by-type', + (defaultFilter, context) => { + // Customize based on relationship type + if (context.rel_type === 'post-to-user') { + return (item, result) => { + return { + ...item, + url: '', + info: `User: ${result.name || result.username}`, + }; + }; + } + return defaultFilter; + } +); +``` + +#### Accessing Additional REST API Fields + +To display additional fields from the REST API, you may need to register them first in PHP: + +```php +add_action('rest_api_init', function() { + register_rest_field('search-result', 'excerpt', array( + 'get_callback' => function($post) { + return get_the_excerpt($post['id']); + }, + 'update_callback' => null, + 'schema' => null, + )); +}); +``` + +Then use them in your filter: + +```javascript +addFilter( + 'contentConnect.searchResultFilter', + 'my-project/add-excerpt', + (defaultFilter, context) => { + return (item, result) => { + // result.excerpt is a string from the REST API search endpoint + return { + ...item, + url: '', + info: `ID: ${result.id}
${result.excerpt || ''}`, + }; + }; + } +); + +addFilter( + 'contentConnect.pickedItemFilter', + 'my-project/add-excerpt-to-picked', + (defaultFilter, context) => { + return (item, result) => { + // result.excerpt.rendered is from getEntityRecord (core WordPress entity) + return { + ...item, + url: '', + info: `ID: ${result.id}
${result.excerpt?.rendered || ''}`, + }; + }; + } +); +``` + +#### Custom Preview Component + +Customize the React component used to render picked items: + +```javascript +import { addFilter } from '@wordpress/hooks'; +import { __experimentalText as Text } from '@wordpress/components'; +import { decodeEntities } from '@wordpress/html-entities'; + +addFilter( + 'contentConnect.pickedItemPreviewComponent', + 'my-project/custom-preview', + (defaultComponent, context) => { + return ({ item }) => { + const decodedTitle = decodeEntities(item.title); + return ( + + {decodedTitle} + + ); + }; + } +); +``` + +### Best Practices + +1. **Return Plain Functions**: Filter callbacks should return plain functions, not React hooks. The component handles memoization internally. For `pickedItemPreviewComponent`, return a React component function. +2. **Check Context**: Use the context object to conditionally apply customizations based on relationship key, type, or post ID +3. **Return Default When Appropriate**: If your filter doesn't apply to a specific context, return the `defaultFilter` or `defaultComponent` to maintain default behavior +4. **Type Safety**: Use TypeScript types when available to ensure type safety + ## Support Level **Stable:** 10up is not planning to develop any new features for this, but will still respond to bug reports and security concerns. We welcome PRs, but any that include new features should be small and easy to integrate and should not include breaking changes. We otherwise intend to keep this tested up to the most recent version of WordPress. @@ -486,7 +721,6 @@ A complete listing of all notable changes to WP Content Connect are documented i Please read [CODE_OF_CONDUCT.md](https://github.com/10up/wp-content-connect/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct, [CONTRIBUTING.md](https://github.com/10up/wp-content-connect/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us, and [CREDITS.md](https://github.com/10up/wp-content-connect/blob/develop/CREDITS.md) for a listing of maintainers of, contributors to, and libraries used by WP Content Connect. - ## Like what you see?

diff --git a/Requirements.txt b/Requirements.txt deleted file mode 100644 index 0d8426b..0000000 --- a/Requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -PHP 5.6 is required to use the bundled autoloader (in place of composer autoloader). Composer sets its own requirements -for minimum php version. diff --git a/assets/css/admin-styles.css b/assets/css/admin-styles.css new file mode 100644 index 0000000..febab73 --- /dev/null +++ b/assets/css/admin-styles.css @@ -0,0 +1,4 @@ +.content-connect-relationship-manager .block-editor-link-control__search-items { + max-height: 350px; + overflow-y: auto; +} diff --git a/assets/js/classic-editor.tsx b/assets/js/classic-editor.tsx new file mode 100644 index 0000000..93ee138 --- /dev/null +++ b/assets/js/classic-editor.tsx @@ -0,0 +1,93 @@ +/** + * External dependencies + */ +import React from 'react'; + +/** + * WordPress dependencies + */ +import { createRoot } from '@wordpress/element'; +import domReady from '@wordpress/dom-ready'; +import { select, dispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { RelationshipManager } from './components/RelationshipManager'; +import { store, persistContentConnectChanges } from './store'; +import { ContentConnectRelationship } from './store/types'; + +/** + * Register the relationships panels. + */ +const registerPanels = () => { + const containers = document.querySelectorAll('[data-content-connect]'); + if (!containers.length) { + return; + } + + const postForm = document.querySelector('#post'); + if (!postForm) { + return; + } + + let postId: number | null = null; + const relationshipsMap: Record = {}; + + containers.forEach((container) => { + const { postId: containerPostId, relationship: relationshipJson } = container.dataset; + + let relationshipData: ContentConnectRelationship | false = false; + try { + relationshipData = JSON.parse(relationshipJson || '') as ContentConnectRelationship; + } catch (e) { + console.error('Invalid JSON in data-content-connect:', e); + return; + } + + if (container && relationshipData) { + postId = parseInt(containerPostId ?? '0', 10); + relationshipsMap[relationshipData.rel_key] = relationshipData; + + const root = createRoot(container); + root.render( + + ); + } + }); + + // Initialize relationships in the store + if (postId) { + const relationships: Record = {}; + Object.values(relationshipsMap).forEach((rel) => { + relationships[rel.rel_key] = rel; + }); + dispatch(store).setRelationships(postId, relationships); + } + + // Hook into form submission to persist relationships before save + postForm.addEventListener('submit', async (event) => { + const dirtyEntityIds = select(store).getDirtyEntityIds(); + console.log('dirtyEntityIds', dirtyEntityIds); + + // Only intercept if there are unsaved relationship changes + if (dirtyEntityIds.length > 0) { + event.preventDefault(); + event.stopPropagation(); + + try { + await persistContentConnectChanges(); + postForm.submit(); + } catch (error) { + console.error('Failed to persist Content Connect changes:', error); // eslint-disable-line no-console + postForm.submit(); + } + } + }); +} + +domReady(registerPanels); diff --git a/assets/js/components/RelationshipManager/index.tsx b/assets/js/components/RelationshipManager/index.tsx new file mode 100644 index 0000000..228b319 --- /dev/null +++ b/assets/js/components/RelationshipManager/index.tsx @@ -0,0 +1,170 @@ +/** + * External dependencies + */ +import React from 'react'; +import { ContentPicker } from '@10up/block-components'; +import type { WP_REST_API_Search_Result, WP_REST_API_User } from 'wp-types'; + +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; +import { applyFilters } from '@wordpress/hooks'; +import { addQueryArgs } from '@wordpress/url'; +import type { Post, User } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { store } from '../../store'; +import { ContentConnectRelationship } from '../../store/types'; + +/** + * Normalized suggestion type for search results. + */ +type NormalizedSuggestion = { + id: number; + subtype: string; + title: string; + type: string; + url: string; +}; + +/** + * Picked item type. + */ +type PickedItemType = { + id: number; + type: string; + uuid: string; + title: string; + url?: string; +}; + +/** + * Search result filter function type. + */ +type SearchResultFilter = ( + item: NormalizedSuggestion, + originalResult: WP_REST_API_Search_Result | WP_REST_API_User +) => NormalizedSuggestion; + +/** + * Picked item filter function type. + */ +type PickedItemFilter = ( + item: Partial, + originalResult: Post | Term | User +) => Partial; + +/** + * Picked item preview component type. + */ +type PickedItemPreviewComponent = React.ComponentType<{ item: PickedItemType }>; + +type RelationshipManagerProps = { + postId: number | null; + relationship: ContentConnectRelationship; +}; + +type FilterContext = { + rel_key: string; + rel_type: string; + postId: number | null; + mode: 'post' | 'user' | 'term'; +}; + +/** + * Default search result filter. + */ +const defaultSearchResultFilter: SearchResultFilter = (item: NormalizedSuggestion) => { + return item; +}; + +/** + * Default picked item filter. + */ +const defaultPickedItemFilter: PickedItemFilter = (item: Partial) => { + return item; +}; + +export function RelationshipManager({ postId, relationship }: RelationshipManagerProps) { + const { updateRelatedEntities } = useDispatch(store); + + const { relatedEntities } = useSelect((select) => ({ + relatedEntities: select(store).getRelatedEntities(postId, { + rel_key: relationship.rel_key, + rel_type: relationship.rel_type, + }), + }), [postId, relationship.rel_key, relationship.rel_type]); + + const handleChange = async (newEntities: PickedItemType[]) => { + await updateRelatedEntities( + postId, + relationship.rel_key, + relationship.rel_type, + newEntities + ); + }; + + const mode = (relationship?.object_type ?? 'post') as 'post' | 'user' | 'term'; + + const filterContext: FilterContext = useMemo( + () => ({ + rel_key: relationship.rel_key, + rel_type: relationship.rel_type, + postId, + mode, + }), + [relationship.rel_key, relationship.rel_type, postId, mode] + ); + + const searchResultFilter = useMemo(() => { + return applyFilters( + 'contentConnect.searchResultFilter', + defaultSearchResultFilter, + filterContext + ) as SearchResultFilter; + }, [filterContext]); + + const pickedItemFilter = useMemo(() => { + return applyFilters( + 'contentConnect.pickedItemFilter', + defaultPickedItemFilter, + filterContext + ) as PickedItemFilter; + }, [filterContext]); + + const pickedItemPreviewComponent = useMemo(() => { + return applyFilters( + 'contentConnect.pickedItemPreviewComponent', + undefined, + filterContext + ) as PickedItemPreviewComponent | undefined; + }, [filterContext]); + + return ( +

+ { + if (relationship?.rel_key) { + return addQueryArgs(query, { + content_connect: relationship.rel_key + }); + } + return query; + }} + searchResultFilter={searchResultFilter} + pickedItemFilter={pickedItemFilter} + PickedItemPreviewComponent={pickedItemPreviewComponent} + /> +
+ ); +} diff --git a/assets/js/components/RelationshipsPanel/index.tsx b/assets/js/components/RelationshipsPanel/index.tsx new file mode 100644 index 0000000..760d96c --- /dev/null +++ b/assets/js/components/RelationshipsPanel/index.tsx @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import React from 'react'; + +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as editorStore } from '@wordpress/editor'; +import { PluginDocumentSettingPanel } from '@wordpress/edit-post'; + +/** + * Internal dependencies + */ +import { store } from '../../store'; +import { RelationshipManager } from '../RelationshipManager'; + +export function RelationshipsPanel() { + const { postId, relationships } = useSelect((select) => { + const postId = select(editorStore).getCurrentPostId(); + const relationships = select(store).getRelationships(postId); + + return { + postId, + relationships, + }; + }, []); + + if (!relationships || Object.keys(relationships).length === 0) { + return null; + } + + const enabledRelationships = Object.values(relationships).filter( + (relationship) => relationship.enable_ui === true + ); + + if (enabledRelationships.length === 0) { + return null; + } + + return ( + <> + {enabledRelationships.map((relationship) => ( + + + + ))} + + ); +} diff --git a/assets/js/content-connect.js b/assets/js/content-connect.js deleted file mode 100644 index 11de7d6..0000000 --- a/assets/js/content-connect.js +++ /dev/null @@ -1,12445 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i a {\n display: block;\n position: relative;\n padding: 8px 20px;\n margin: 0;\n color: #0073aa;\n line-height: 18px;\n font-size: 14px;\n text-decoration: none;\n cursor: pointer; }\n /* line 121, stdin */\n .vtab-menu > a:hover {\n color: #0073aa;\n background: rgba(0, 0, 0, 0.04); }\n\n/* line 127, stdin */\n.vtab-frame-title {\n height: 50px;\n display: flex;\n align-items: center; }\n /* line 132, stdin */\n .vtab-frame-title i {\n margin-right: 0.5em; }\n /* line 136, stdin */\n .vtab-frame-title h1 {\n padding: 0;\n font-size: 22px;\n line-height: 50px;\n margin: 0; }\n\n/* line 144, stdin */\n.vtab-frame-content {\n background: #fff;\n bottom: 61px; }\n\n/* line 153, stdin */\n.vtab-frame-toolbar {\n border-top: 1px solid #ddd;\n height: 60px; }\n\n/* line 158, stdin */\n.vtab-grid-list {\n display: flex;\n flex-wrap: wrap; }\n\n/* line 163, stdin */\n.vtab-grid-list-item {\n margin: 10px;\n box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1);\n background: #eee;\n cursor: pointer;\n text-align: center;\n width: 150px;\n height: 150px;\n position: relative; }\n\n/* line 174, stdin */\n.vtab-grid-list-item-icon {\n height: 120px;\n font-size: 64px;\n display: flex;\n align-items: center;\n justify-content: center; }\n /* line 181, stdin */\n .vtab-grid-list-item-icon .dashicons {\n font-size: inherit;\n height: auto;\n width: auto;\n max-width: 80%;\n max-height: 80%; }\n\n/* line 190, stdin */\n.vtab-grid-list-item-title {\n box-sizing: border-box;\n position: absolute;\n bottom: 0;\n left: 0;\n width: 100%;\n margin: 0;\n line-height: 1.2;\n padding: 8px;\n overflow: hidden;\n max-height: 100%;\n word-wrap: break-word;\n text-align: center;\n font-weight: bold;\n background: rgba(255, 255, 255, 0.8);\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15); }") -;(function(){ -'use strict'; - -var _stringify = require('babel-runtime/core-js/json/stringify'); - -var _stringify2 = _interopRequireDefault(_stringify); - -var _assign = require('babel-runtime/core-js/object/assign'); - -var _assign2 = _interopRequireDefault(_assign); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var PickerList = require('./components/picker-list.vue'); -var PickerSearch = require('./components/picker-search.vue'); - -module.exports = { - data: function data() { - return (0, _assign2.default)({}, { - "activeRelationship": window.ContentConnectData.relationships[0], - "searchResults": [], - "searching": false, - "searchErrorMessage": "", - "searchText": "", - "prevPages": false, - "morePages": false, - "currentPage": 1 - }, window.ContentConnectData); - }, - components: { - PickerList: PickerList, - PickerSearch: PickerSearch - }, - computed: { - saveData: function saveData() { - var data = {}, - relationship, - i, - j; - - for (i = 0; i < this.relationships.length; i++) { - relationship = this.relationships[i]; - - data[relationship.relid] = { - "reltype": relationship.reltype, - "relid": relationship.relid, - "add_items": [] - }; - - for (j = 0; j < relationship.selected.length; j++) { - data[relationship.relid].add_items.push(relationship.selected[j].ID); - } - } - - return (0, _stringify2.default)(data); - } - }, - methods: { - activeMenuItem: function activeMenuItem(relationship) { - return { - active: relationship === this.activeRelationship - }; - }, - setActiveRelationship: function setActiveRelationship(relationship) { - this.activeRelationship = relationship; - - this.searchResults = []; - }, - search: function search(searchText) { - this.prevPages = false; - this.morePages = false; - this.curentPage = 1; - this.searchText = searchText; - - this.sendSearchRequest(); - }, - prevPage: function prevPage() { - if (this.prevPages !== true) { - return; - } - - if (this.currentPage <= 1) { - this.prevPages = false; - return; - } - - this.currentPage--; - this.sendSearchRequest(); - }, - nextPage: function nextPage() { - if (this.morePages !== true) { - return; - } - - this.currentPage++; - - this.sendSearchRequest(); - }, - sendSearchRequest: function sendSearchRequest() { - var _this = this; - - this.searching = true; - this.searchErrorMessage = ''; - this.searchResults = []; - - this.$http.post(this.endpoints.search, { - "nonce": this.nonces.search, - "object_type": this.activeRelationship.object_type, - "post_type": this.activeRelationship.post_type, - "search": this.searchText, - "paged": this.currentPage, - "relationship_name": this.activeRelationship.name, - "current_post_id": this.activeRelationship.current_post_id - }).then(function (response) { - var i, result; - - _this.searching = false; - _this.didSearch = true; - - _this.searchResults = []; - _this.searchErrorMessage = ''; - - _this.prevPages = response.body.prev_pages; - _this.morePages = response.body.more_pages; - - for (i = 0; i < response.body.data.length; i++) { - result = response.body.data[i]; - - if (_this.isSelected(result.ID) === false) { - result.added = false; - } else { - result.added = true; - } - _this.searchResults.push(result); - } - - if (_this.searchResults.length === 0) { - _this.searchErrorMessage = "Your search returned no results"; - } - }, function (response) { - _this.searching = false; - _this.didSearch = true; - - _this.searchErrorMessage = "An error occurred. Please try your search again"; - }); - }, - isSelected: function isSelected(id) { - var key, item; - for (key in this.activeRelationship.selected) { - item = this.activeRelationship.selected[key]; - if (parseInt(item.ID, 10) === parseInt(id, 10)) { - return true; - } - } - - return false; - }, - addSearchItem: function addSearchItem(item) { - this.activeRelationship.selected.push(item); - var index = this.searchResults.indexOf(item); - this.searchResults[index].added = true; - }, - reorderItems: function reorderItems(items) { - this.activeRelationship.selected = items; - }, - deleteItem: function deleteItem(item) { - for (var i in this.searchResults) { - if (this.searchResults[i].ID === item.ID) { - this.searchResults[i].added = false; - break; - } - } - - var index = this.activeRelationship.selected.indexOf(item); - this.activeRelationship.selected.splice(index, 1); - } - } -}; -})() -if (module.exports.__esModule) module.exports = module.exports.default -var __vue__options__ = (typeof module.exports === "function"? module.exports.options: module.exports) -if (__vue__options__.functional) {console.error("[vueify] functional components are not supported and should be defined in plain js files using render functions.")} -__vue__options__.render = function render () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"tenup-content-connect vtab-frame"},[(_vm.relationships.length > 1)?_c('div',{staticClass:"vtab-left"},[_c('div',{staticClass:"vtab-frame-menu"},[_c('div',{staticClass:"vtab-menu"},[(_vm.relationships.length)?_vm._l((_vm.relationships),function(relationship){return _c('a',{staticClass:"vtab-menu-item",class:_vm.activeMenuItem(relationship),on:{"click":function($event){$event.preventDefault();_vm.setActiveRelationship(relationship)}}},[_vm._v("\n\t\t\t\t\t\t"+_vm._s(relationship.labels.name)+"\n\t\t\t\t\t")])}):_vm._e()],2)])]):_vm._e(),_vm._v(" "),_c('div',{staticClass:"vtab-right"},[(_vm.activeRelationship)?[_c('div',{staticClass:"vtab-frame-title"},[_c('h1',[_vm._v(_vm._s(_vm.activeRelationship.labels.name))])]),_vm._v(" "),_c('div',{staticClass:"vtab-frame-content"},[_c('div',{staticClass:"vtab-content-area"},[_c('picker-list',{attrs:{"sortable":_vm.activeRelationship.sortable,"items":_vm.activeRelationship.selected},on:{"reorder-items":_vm.reorderItems,"delete-item":_vm.deleteItem}}),_vm._v(" "),_c('picker-search',{attrs:{"results":_vm.searchResults,"searching":_vm.searching,"didsearch":_vm.didSearch,"searcherror":_vm.searchErrorMessage,"prevPages":_vm.prevPages,"morePages":_vm.morePages},on:{"add-item":_vm.addSearchItem,"search":_vm.search,"next-page":_vm.nextPage,"prev-page":_vm.prevPage}})],1)])]:_vm._e()],2),_vm._v(" "),_c('br'),_vm._v(" "),_c('div',[_c('input',{directives:[{name:"model",rawName:"v-model",value:(_vm.saveData),expression:"saveData"}],attrs:{"type":"hidden","name":"tenup-content-connect-relationships"},domProps:{"value":(_vm.saveData)},on:{"input":function($event){if($event.target.composing){ return; }_vm.saveData=$event.target.value}}})])])} -__vue__options__.staticRenderFns = [] -if (module.hot) {(function () { var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - module.hot.accept() - module.hot.dispose(__vueify_style_dispose__) - if (!module.hot.data) { - hotAPI.createRecord("data-v-4fd92f7a", __vue__options__) - } else { - hotAPI.reload("data-v-4fd92f7a", __vue__options__) - } -})()} -},{"./components/picker-list.vue":2,"./components/picker-search.vue":3,"babel-runtime/core-js/json/stringify":5,"babel-runtime/core-js/object/assign":6,"vue":50,"vue-hot-reload-api":48,"vueify/lib/insert-css":52}],2:[function(require,module,exports){ -var __vueify_style_dispose__ = require("vueify/lib/insert-css").insert("*[data-v-6900689b] {\n\tbox-sizing: border-box;\n}\n\n.content-connect-picker-list-item[data-v-6900689b] {\n\twidth: 100%;\n\tposition: relative;\n\tpadding: 1em 1em 1em 0.5em;\n}\n\n.content-connect-picker-list-item.sortable[data-v-6900689b] {\n\tcursor: move;\n}\n\n.content-connect-picker-list-item[data-v-6900689b]:nth-child(odd) {\n\tbackground-color: #f9f9f9;\n}\n\n.content-connect-picker-list-item.ghost[data-v-6900689b] {\n\topacity: 0.5;\n\tbackground: #c8ebfb;\n}\n\n.content-connect-grab-icon[data-v-6900689b] {\n\tfont-size: 16px;\n\tcolor: #bbb;\n}\n\n.content-connect-delete-button[data-v-6900689b] {\n\tcolor: #a00;\n\tvisibility: hidden;\n\tdisplay: inline-block;\n\tfloat: right;\n\tposition: relative;\n\tcursor: pointer;\n}\n\n.content-connect-delete-button[data-v-6900689b]:hover {\n\tcolor: #dc3232;\n}\n\n.content-connect-picker-list-item:hover .content-connect-delete-button[data-v-6900689b] {\n\tvisibility: visible;\n}") -;(function(){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - - -var draggable = require('vuedraggable'); - -exports.default = { - props: { - items: {}, - sortable: {} - }, - components: { - draggable: draggable - }, - computed: { - localItems: { - get: function get() { - return this.items; - }, - set: function set(items) { - this.$emit('reorder-items', items); - } - } - }, - methods: { - deleteItem: function deleteItem(item) { - this.$emit('delete-item', item); - } - } -}; -})() -if (module.exports.__esModule) module.exports = module.exports.default -var __vue__options__ = (typeof module.exports === "function"? module.exports.options: module.exports) -if (__vue__options__.functional) {console.error("[vueify] functional components are not supported and should be defined in plain js files using render functions.")} -__vue__options__.render = function render () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"content-connect-picker-list-container"},[_c('ul',{staticClass:"content-connect-picker"},[(_vm.sortable === true)?_c('draggable',{attrs:{"options":{ghostClass: 'ghost'}},on:{"start":function($event){_vm.drag=true},"end":function($event){_vm.drag=false}},model:{value:(_vm.localItems),callback:function ($$v) {_vm.localItems=$$v},expression:"localItems"}},_vm._l((_vm.items),function(item){return _c('li',{staticClass:"content-connect-picker-list-item sortable"},[_c('span',{staticClass:"content-connect-grab-icon dashicons dashicons-move"}),_vm._v(" "),_c('span',{staticClass:"content-connect-selected-item-name"},[_vm._v(_vm._s(item.name))]),_vm._v(" "),_c('span',{staticClass:"delete-item content-connect-delete-button",on:{"click":function($event){$event.preventDefault();_vm.deleteItem(item)}}},[_vm._v("delete")])])})):_vm._e(),_vm._v(" "),_vm._l((_vm.items),function(item){return (_vm.sortable === false)?_c('li',{staticClass:"content-connect-picker-list-item"},[_c('span',{staticClass:"content-connect-selected-item-name"},[_vm._v(_vm._s(item.name))]),_vm._v(" "),_c('span',{staticClass:"delete-item content-connect-delete-button",on:{"click":function($event){$event.preventDefault();_vm.deleteItem(item)}}},[_vm._v("delete")])]):_vm._e()})],2)])} -__vue__options__.staticRenderFns = [] -__vue__options__._scopeId = "data-v-6900689b" -if (module.hot) {(function () { var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - module.hot.accept() - module.hot.dispose(__vueify_style_dispose__) - if (!module.hot.data) { - hotAPI.createRecord("data-v-6900689b", __vue__options__) - } else { - hotAPI.reload("data-v-6900689b", __vue__options__) - } -})()} -},{"vue":50,"vue-hot-reload-api":48,"vuedraggable":51,"vueify/lib/insert-css":52}],3:[function(require,module,exports){ -var __vueify_style_dispose__ = require("vueify/lib/insert-css").insert("*[data-v-29630365] {\n\tbox-sizing: border-box;\n}\n\n.content-connect-picker-search-container[data-v-29630365] {\n\tpadding-bottom: 20px;\n}\n\n.content-connect-picker-search-input-label[data-v-29630365] {\n\tdisplay: block;\n}\n\n.content-connect-picker-search-input-container form[data-v-29630365] {\n\tdisplay: flex;\n}\n\n.content-connect-picker-search-input[data-v-29630365] {\n\tflex: 1;\n\tmargin-right: 0.5em;\n}\n\n.content-connect-picker-search-item[data-v-29630365] {\n\twidth: 100%;\n\tposition: relative;\n\tpadding: 1em 1em 1em 0.5em;\n}\n\n.content-connect-picker-search-item.result[data-v-29630365]:nth-child(odd) {\n\tbackground-color: #f9f9f9;\n}\n\n.content-connect-picker-search-item.searching .spinner[data-v-29630365] {\n\tfloat: left;\n\tmargin-top: 0;\n}\n\n.content-connect-already-added[data-v-29630365],\n.content-connect-add-button[data-v-29630365] {\n\tdisplay: inline-block;\n\tfloat: right;\n\tposition: relative;\n}\n\n.content-connect-add-button[data-v-29630365] {\n\tcolor: #0073aa;\n\tcursor: pointer;\n}\n\n.content-connect-already-added[data-v-29630365] {\n\tcolor: #aaa;\n\tfont-style: italic;\n}\n\n.content-connect-add-button[data-v-29630365]:hover {\n\tcolor: #00a0d2;\n}\n\n.content-connect-picker-pagination[data-v-29630365] {\n\theight: 3em;\n\tborder-top: 1px solid #ddd;\n\tpadding-top: 20px;\n}\n\n.content-connect-picker-pagination a[data-v-29630365] {\n\tcursor: pointer;\n}\n\n.content-connect-picker-pagination .next-page[data-v-29630365] {\n\tfloat: right;\n}") -;(function(){ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = { - props: { - results: {}, - searching: false, - searcherror: "", - prevPages: false, - morePages: false - }, - data: function data() { - return { - searchtext: '' - }; - }, - methods: { - search: function search() { - this.$emit('search', this.searchtext); - }, - add: function add(item) { - this.$emit('add-item', item); - }, - nextPage: function nextPage() { - this.$emit('next-page'); - }, - prevPage: function prevPage() { - this.$emit('prev-page'); - } - } -}; -})() -if (module.exports.__esModule) module.exports = module.exports.default -var __vue__options__ = (typeof module.exports === "function"? module.exports.options: module.exports) -if (__vue__options__.functional) {console.error("[vueify] functional components are not supported and should be defined in plain js files using render functions.")} -__vue__options__.render = function render () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"content-connect-picker-search-container"},[_c('label',{staticClass:"content-connect-picker-search-input-label",attrs:{"for":_vm._uid}},[_vm._v("Search")]),_vm._v(" "),_c('div',{staticClass:"content-connect-picker-search-input-container"},[_c('form',{on:{"submit":function($event){$event.preventDefault();_vm.search($event)}}},[_c('input',{directives:[{name:"model",rawName:"v-model",value:(_vm.searchtext),expression:"searchtext"}],staticClass:"content-connect-picker-search-input widefat",attrs:{"type":"text","id":_vm._uid},domProps:{"value":(_vm.searchtext)},on:{"input":function($event){if($event.target.composing){ return; }_vm.searchtext=$event.target.value}}}),_vm._v(" "),_c('button',{staticClass:"button",attrs:{"type":"submit"}},[_vm._v("Search")])])]),_vm._v(" "),_c('ul',{staticClass:"content-connect-picker-search-list"},[_vm._l((_vm.results),function(result){return _c('li',{staticClass:"content-connect-picker-search-item result"},[_c('span',{staticClass:"content-connect-selected-item-name"},[_vm._v(_vm._s(result.name))]),_vm._v(" "),(!result.added)?_c('span',{staticClass:"add-item content-connect-add-button",on:{"click":function($event){$event.preventDefault();$event.stopPropagation();_vm.add(result)}}},[_vm._v("add")]):_vm._e(),_vm._v(" "),(result.added)?_c('span',{staticClass:"add-item content-connect-already-added"},[_vm._v("Added")]):_vm._e()])}),_vm._v(" "),(_vm.searching)?_c('li',{staticClass:"content-connect-picker-search-item searching"},[_vm._m(0)]):_vm._e(),_vm._v(" "),(! _vm.searching && _vm.searcherror.length > 0)?_c('li',{staticClass:"content-connect-picker-search-item error"},[_c('p',{staticClass:"error"},[_vm._v(_vm._s(_vm.searcherror))])]):_vm._e()],2),_vm._v(" "),(! _vm.searching && ( _vm.morePages || _vm.prevPages ))?_c('div',{staticClass:"content-connect-picker-pagination"},[(_vm.prevPages)?_c('a',{staticClass:"prev-page",on:{"click":function($event){$event.preventDefault();$event.stopPropagation();_vm.prevPage()}}},[_vm._v("‹ Previous Page")]):_vm._e(),_vm._v(" "),(_vm.morePages)?_c('a',{staticClass:"next-page",on:{"click":function($event){$event.preventDefault();$event.stopPropagation();_vm.nextPage()}}},[_vm._v("Next Page ›")]):_vm._e()]):_vm._e()])} -__vue__options__.staticRenderFns = [function render () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('p',[_c('span',{staticClass:"spinner is-active"}),_vm._v("\n\t\t\t\tSearching...\n\t\t\t")])}] -__vue__options__._scopeId = "data-v-29630365" -if (module.hot) {(function () { var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - module.hot.accept() - module.hot.dispose(__vueify_style_dispose__) - if (!module.hot.data) { - hotAPI.createRecord("data-v-29630365", __vue__options__) - } else { - hotAPI.reload("data-v-29630365", __vue__options__) - } -})()} -},{"vue":50,"vue-hot-reload-api":48,"vueify/lib/insert-css":52}],4:[function(require,module,exports){ -var Vue = require( 'vue' ); -var App = require( './App.vue' ); - -Vue.use( require( 'vue-resource' ) ); - -// Adds the global wp_rest nonce, so we can auth a user -Vue.http.interceptors.push(function(request, next) { - request.headers.set( 'X-WP-Nonce', ContentConnectData.nonces.wp_rest ); - - next(); -}); - -window.ContentConnectApp = new Vue({ - render: createEle => createEle( App ) -}).$mount( '#tenup-content-connect-app' ); - - -},{"./App.vue":1,"vue":50,"vue-resource":49}],5:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/json/stringify"), __esModule: true }; -},{"core-js/library/fn/json/stringify":8}],6:[function(require,module,exports){ -module.exports = { "default": require("core-js/library/fn/object/assign"), __esModule: true }; -},{"core-js/library/fn/object/assign":9}],7:[function(require,module,exports){ - -},{}],8:[function(require,module,exports){ -var core = require('../../modules/_core'); -var $JSON = core.JSON || (core.JSON = { stringify: JSON.stringify }); -module.exports = function stringify(it) { // eslint-disable-line no-unused-vars - return $JSON.stringify.apply($JSON, arguments); -}; - -},{"../../modules/_core":14}],9:[function(require,module,exports){ -require('../../modules/es6.object.assign'); -module.exports = require('../../modules/_core').Object.assign; - -},{"../../modules/_core":14,"../../modules/es6.object.assign":44}],10:[function(require,module,exports){ -module.exports = function (it) { - if (typeof it != 'function') throw TypeError(it + ' is not a function!'); - return it; -}; - -},{}],11:[function(require,module,exports){ -var isObject = require('./_is-object'); -module.exports = function (it) { - if (!isObject(it)) throw TypeError(it + ' is not an object!'); - return it; -}; - -},{"./_is-object":27}],12:[function(require,module,exports){ -// false -> Array#indexOf -// true -> Array#includes -var toIObject = require('./_to-iobject'); -var toLength = require('./_to-length'); -var toAbsoluteIndex = require('./_to-absolute-index'); -module.exports = function (IS_INCLUDES) { - return function ($this, el, fromIndex) { - var O = toIObject($this); - var length = toLength(O.length); - var index = toAbsoluteIndex(fromIndex, length); - var value; - // Array#includes uses SameValueZero equality algorithm - // eslint-disable-next-line no-self-compare - if (IS_INCLUDES && el != el) while (length > index) { - value = O[index++]; - // eslint-disable-next-line no-self-compare - if (value != value) return true; - // Array#indexOf ignores holes, Array#includes - not - } else for (;length > index; index++) if (IS_INCLUDES || index in O) { - if (O[index] === el) return IS_INCLUDES || index || 0; - } return !IS_INCLUDES && -1; - }; -}; - -},{"./_to-absolute-index":37,"./_to-iobject":39,"./_to-length":40}],13:[function(require,module,exports){ -var toString = {}.toString; - -module.exports = function (it) { - return toString.call(it).slice(8, -1); -}; - -},{}],14:[function(require,module,exports){ -var core = module.exports = { version: '2.5.1' }; -if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef - -},{}],15:[function(require,module,exports){ -// optional / simple context binding -var aFunction = require('./_a-function'); -module.exports = function (fn, that, length) { - aFunction(fn); - if (that === undefined) return fn; - switch (length) { - case 1: return function (a) { - return fn.call(that, a); - }; - case 2: return function (a, b) { - return fn.call(that, a, b); - }; - case 3: return function (a, b, c) { - return fn.call(that, a, b, c); - }; - } - return function (/* ...args */) { - return fn.apply(that, arguments); - }; -}; - -},{"./_a-function":10}],16:[function(require,module,exports){ -// 7.2.1 RequireObjectCoercible(argument) -module.exports = function (it) { - if (it == undefined) throw TypeError("Can't call method on " + it); - return it; -}; - -},{}],17:[function(require,module,exports){ -// Thank's IE8 for his funny defineProperty -module.exports = !require('./_fails')(function () { - return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; -}); - -},{"./_fails":21}],18:[function(require,module,exports){ -var isObject = require('./_is-object'); -var document = require('./_global').document; -// typeof document.createElement is 'object' in old IE -var is = isObject(document) && isObject(document.createElement); -module.exports = function (it) { - return is ? document.createElement(it) : {}; -}; - -},{"./_global":22,"./_is-object":27}],19:[function(require,module,exports){ -// IE 8- don't enum bug keys -module.exports = ( - 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' -).split(','); - -},{}],20:[function(require,module,exports){ -var global = require('./_global'); -var core = require('./_core'); -var ctx = require('./_ctx'); -var hide = require('./_hide'); -var PROTOTYPE = 'prototype'; - -var $export = function (type, name, source) { - var IS_FORCED = type & $export.F; - var IS_GLOBAL = type & $export.G; - var IS_STATIC = type & $export.S; - var IS_PROTO = type & $export.P; - var IS_BIND = type & $export.B; - var IS_WRAP = type & $export.W; - var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); - var expProto = exports[PROTOTYPE]; - var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; - var key, own, out; - if (IS_GLOBAL) source = name; - for (key in source) { - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if (own && key in exports) continue; - // export native or passed - out = own ? target[key] : source[key]; - // prevent global pollution for namespaces - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] - // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) - // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? (function (C) { - var F = function (a, b, c) { - if (this instanceof C) { - switch (arguments.length) { - case 0: return new C(); - case 1: return new C(a); - case 2: return new C(a, b); - } return new C(a, b, c); - } return C.apply(this, arguments); - }; - F[PROTOTYPE] = C[PROTOTYPE]; - return F; - // make static versions for prototype methods - })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; - // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - if (IS_PROTO) { - (exports.virtual || (exports.virtual = {}))[key] = out; - // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); - } - } -}; -// type bitmap -$export.F = 1; // forced -$export.G = 2; // global -$export.S = 4; // static -$export.P = 8; // proto -$export.B = 16; // bind -$export.W = 32; // wrap -$export.U = 64; // safe -$export.R = 128; // real proto method for `library` -module.exports = $export; - -},{"./_core":14,"./_ctx":15,"./_global":22,"./_hide":24}],21:[function(require,module,exports){ -module.exports = function (exec) { - try { - return !!exec(); - } catch (e) { - return true; - } -}; - -},{}],22:[function(require,module,exports){ -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self - // eslint-disable-next-line no-new-func - : Function('return this')(); -if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef - -},{}],23:[function(require,module,exports){ -var hasOwnProperty = {}.hasOwnProperty; -module.exports = function (it, key) { - return hasOwnProperty.call(it, key); -}; - -},{}],24:[function(require,module,exports){ -var dP = require('./_object-dp'); -var createDesc = require('./_property-desc'); -module.exports = require('./_descriptors') ? function (object, key, value) { - return dP.f(object, key, createDesc(1, value)); -} : function (object, key, value) { - object[key] = value; - return object; -}; - -},{"./_descriptors":17,"./_object-dp":29,"./_property-desc":34}],25:[function(require,module,exports){ -module.exports = !require('./_descriptors') && !require('./_fails')(function () { - return Object.defineProperty(require('./_dom-create')('div'), 'a', { get: function () { return 7; } }).a != 7; -}); - -},{"./_descriptors":17,"./_dom-create":18,"./_fails":21}],26:[function(require,module,exports){ -// fallback for non-array-like ES3 and non-enumerable old V8 strings -var cof = require('./_cof'); -// eslint-disable-next-line no-prototype-builtins -module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { - return cof(it) == 'String' ? it.split('') : Object(it); -}; - -},{"./_cof":13}],27:[function(require,module,exports){ -module.exports = function (it) { - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; - -},{}],28:[function(require,module,exports){ -'use strict'; -// 19.1.2.1 Object.assign(target, source, ...) -var getKeys = require('./_object-keys'); -var gOPS = require('./_object-gops'); -var pIE = require('./_object-pie'); -var toObject = require('./_to-object'); -var IObject = require('./_iobject'); -var $assign = Object.assign; - -// should work with symbols and should have deterministic property order (V8 bug) -module.exports = !$assign || require('./_fails')(function () { - var A = {}; - var B = {}; - // eslint-disable-next-line no-undef - var S = Symbol(); - var K = 'abcdefghijklmnopqrst'; - A[S] = 7; - K.split('').forEach(function (k) { B[k] = k; }); - return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K; -}) ? function assign(target, source) { // eslint-disable-line no-unused-vars - var T = toObject(target); - var aLen = arguments.length; - var index = 1; - var getSymbols = gOPS.f; - var isEnum = pIE.f; - while (aLen > index) { - var S = IObject(arguments[index++]); - var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S); - var length = keys.length; - var j = 0; - var key; - while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key]; - } return T; -} : $assign; - -},{"./_fails":21,"./_iobject":26,"./_object-gops":30,"./_object-keys":32,"./_object-pie":33,"./_to-object":41}],29:[function(require,module,exports){ -var anObject = require('./_an-object'); -var IE8_DOM_DEFINE = require('./_ie8-dom-define'); -var toPrimitive = require('./_to-primitive'); -var dP = Object.defineProperty; - -exports.f = require('./_descriptors') ? Object.defineProperty : function defineProperty(O, P, Attributes) { - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if (IE8_DOM_DEFINE) try { - return dP(O, P, Attributes); - } catch (e) { /* empty */ } - if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); - if ('value' in Attributes) O[P] = Attributes.value; - return O; -}; - -},{"./_an-object":11,"./_descriptors":17,"./_ie8-dom-define":25,"./_to-primitive":42}],30:[function(require,module,exports){ -exports.f = Object.getOwnPropertySymbols; - -},{}],31:[function(require,module,exports){ -var has = require('./_has'); -var toIObject = require('./_to-iobject'); -var arrayIndexOf = require('./_array-includes')(false); -var IE_PROTO = require('./_shared-key')('IE_PROTO'); - -module.exports = function (object, names) { - var O = toIObject(object); - var i = 0; - var result = []; - var key; - for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); - // Don't enum bug & hidden keys - while (names.length > i) if (has(O, key = names[i++])) { - ~arrayIndexOf(result, key) || result.push(key); - } - return result; -}; - -},{"./_array-includes":12,"./_has":23,"./_shared-key":35,"./_to-iobject":39}],32:[function(require,module,exports){ -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = require('./_object-keys-internal'); -var enumBugKeys = require('./_enum-bug-keys'); - -module.exports = Object.keys || function keys(O) { - return $keys(O, enumBugKeys); -}; - -},{"./_enum-bug-keys":19,"./_object-keys-internal":31}],33:[function(require,module,exports){ -exports.f = {}.propertyIsEnumerable; - -},{}],34:[function(require,module,exports){ -module.exports = function (bitmap, value) { - return { - enumerable: !(bitmap & 1), - configurable: !(bitmap & 2), - writable: !(bitmap & 4), - value: value - }; -}; - -},{}],35:[function(require,module,exports){ -var shared = require('./_shared')('keys'); -var uid = require('./_uid'); -module.exports = function (key) { - return shared[key] || (shared[key] = uid(key)); -}; - -},{"./_shared":36,"./_uid":43}],36:[function(require,module,exports){ -var global = require('./_global'); -var SHARED = '__core-js_shared__'; -var store = global[SHARED] || (global[SHARED] = {}); -module.exports = function (key) { - return store[key] || (store[key] = {}); -}; - -},{"./_global":22}],37:[function(require,module,exports){ -var toInteger = require('./_to-integer'); -var max = Math.max; -var min = Math.min; -module.exports = function (index, length) { - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); -}; - -},{"./_to-integer":38}],38:[function(require,module,exports){ -// 7.1.4 ToInteger -var ceil = Math.ceil; -var floor = Math.floor; -module.exports = function (it) { - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); -}; - -},{}],39:[function(require,module,exports){ -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = require('./_iobject'); -var defined = require('./_defined'); -module.exports = function (it) { - return IObject(defined(it)); -}; - -},{"./_defined":16,"./_iobject":26}],40:[function(require,module,exports){ -// 7.1.15 ToLength -var toInteger = require('./_to-integer'); -var min = Math.min; -module.exports = function (it) { - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 -}; - -},{"./_to-integer":38}],41:[function(require,module,exports){ -// 7.1.13 ToObject(argument) -var defined = require('./_defined'); -module.exports = function (it) { - return Object(defined(it)); -}; - -},{"./_defined":16}],42:[function(require,module,exports){ -// 7.1.1 ToPrimitive(input [, PreferredType]) -var isObject = require('./_is-object'); -// instead of the ES6 spec version, we didn't implement @@toPrimitive case -// and the second argument - flag - preferred type is a string -module.exports = function (it, S) { - if (!isObject(it)) return it; - var fn, val; - if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; - if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - throw TypeError("Can't convert object to primitive value"); -}; - -},{"./_is-object":27}],43:[function(require,module,exports){ -var id = 0; -var px = Math.random(); -module.exports = function (key) { - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); -}; - -},{}],44:[function(require,module,exports){ -// 19.1.3.1 Object.assign(target, source) -var $export = require('./_export'); - -$export($export.S + $export.F, 'Object', { assign: require('./_object-assign') }); - -},{"./_export":20,"./_object-assign":28}],45:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],46:[function(require,module,exports){ -/**! - * Sortable - * @author RubaXa - * @license MIT - */ - -(function sortableModule(factory) { - "use strict"; - - if (typeof define === "function" && define.amd) { - define(factory); - } - else if (typeof module != "undefined" && typeof module.exports != "undefined") { - module.exports = factory(); - } - else { - /* jshint sub:true */ - window["Sortable"] = factory(); - } -})(function sortableFactory() { - "use strict"; - - if (typeof window == "undefined" || !window.document) { - return function sortableError() { - throw new Error("Sortable.js requires a window with a document"); - }; - } - - var dragEl, - parentEl, - ghostEl, - cloneEl, - rootEl, - nextEl, - lastDownEl, - - scrollEl, - scrollParentEl, - scrollCustomFn, - - lastEl, - lastCSS, - lastParentCSS, - - oldIndex, - newIndex, - - activeGroup, - putSortable, - - autoScroll = {}, - - tapEvt, - touchEvt, - - moved, - - /** @const */ - R_SPACE = /\s+/g, - R_FLOAT = /left|right|inline/, - - expando = 'Sortable' + (new Date).getTime(), - - win = window, - document = win.document, - parseInt = win.parseInt, - - $ = win.jQuery || win.Zepto, - Polymer = win.Polymer, - - captureMode = false, - - supportDraggable = !!('draggable' in document.createElement('div')), - supportCssPointerEvents = (function (el) { - // false when IE11 - if (!!navigator.userAgent.match(/Trident.*rv[ :]?11\./)) { - return false; - } - el = document.createElement('x'); - el.style.cssText = 'pointer-events:auto'; - return el.style.pointerEvents === 'auto'; - })(), - - _silent = false, - - abs = Math.abs, - min = Math.min, - - savedInputChecked = [], - touchDragOverListeners = [], - - _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) { - // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 - if (rootEl && options.scroll) { - var _this = rootEl[expando], - el, - rect, - sens = options.scrollSensitivity, - speed = options.scrollSpeed, - - x = evt.clientX, - y = evt.clientY, - - winWidth = window.innerWidth, - winHeight = window.innerHeight, - - vx, - vy, - - scrollOffsetX, - scrollOffsetY - ; - - // Delect scrollEl - if (scrollParentEl !== rootEl) { - scrollEl = options.scroll; - scrollParentEl = rootEl; - scrollCustomFn = options.scrollFn; - - if (scrollEl === true) { - scrollEl = rootEl; - - do { - if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || - (scrollEl.offsetHeight < scrollEl.scrollHeight) - ) { - break; - } - /* jshint boss:true */ - } while (scrollEl = scrollEl.parentNode); - } - } - - if (scrollEl) { - el = scrollEl; - rect = scrollEl.getBoundingClientRect(); - vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); - vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); - } - - - if (!(vx || vy)) { - vx = (winWidth - x <= sens) - (x <= sens); - vy = (winHeight - y <= sens) - (y <= sens); - - /* jshint expr:true */ - (vx || vy) && (el = win); - } - - - if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { - autoScroll.el = el; - autoScroll.vx = vx; - autoScroll.vy = vy; - - clearInterval(autoScroll.pid); - - if (el) { - autoScroll.pid = setInterval(function () { - scrollOffsetY = vy ? vy * speed : 0; - scrollOffsetX = vx ? vx * speed : 0; - - if ('function' === typeof(scrollCustomFn)) { - return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt); - } - - if (el === win) { - win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY); - } else { - el.scrollTop += scrollOffsetY; - el.scrollLeft += scrollOffsetX; - } - }, 24); - } - } - } - }, 30), - - _prepareGroup = function (options) { - function toFn(value, pull) { - if (value === void 0 || value === true) { - value = group.name; - } - - if (typeof value === 'function') { - return value; - } else { - return function (to, from) { - var fromGroup = from.options.group.name; - - return pull - ? value - : value && (value.join - ? value.indexOf(fromGroup) > -1 - : (fromGroup == value) - ); - }; - } - } - - var group = {}; - var originalGroup = options.group; - - if (!originalGroup || typeof originalGroup != 'object') { - originalGroup = {name: originalGroup}; - } - - group.name = originalGroup.name; - group.checkPull = toFn(originalGroup.pull, true); - group.checkPut = toFn(originalGroup.put); - group.revertClone = originalGroup.revertClone; - - options.group = group; - } - ; - - - /** - * @class Sortable - * @param {HTMLElement} el - * @param {Object} [options] - */ - function Sortable(el, options) { - if (!(el && el.nodeType && el.nodeType === 1)) { - throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el); - } - - this.el = el; // root element - this.options = options = _extend({}, options); - - - // Export instance - el[expando] = this; - - // Default options - var defaults = { - group: Math.random(), - sort: true, - disabled: false, - store: null, - handle: null, - scroll: true, - scrollSensitivity: 30, - scrollSpeed: 10, - draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*', - ghostClass: 'sortable-ghost', - chosenClass: 'sortable-chosen', - dragClass: 'sortable-drag', - ignore: 'a, img', - filter: null, - preventOnFilter: true, - animation: 0, - setData: function (dataTransfer, dragEl) { - dataTransfer.setData('Text', dragEl.textContent); - }, - dropBubble: false, - dragoverBubble: false, - dataIdAttr: 'data-id', - delay: 0, - forceFallback: false, - fallbackClass: 'sortable-fallback', - fallbackOnBody: false, - fallbackTolerance: 0, - fallbackOffset: {x: 0, y: 0} - }; - - - // Set default options - for (var name in defaults) { - !(name in options) && (options[name] = defaults[name]); - } - - _prepareGroup(options); - - // Bind all private methods - for (var fn in this) { - if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { - this[fn] = this[fn].bind(this); - } - } - - // Setup drag mode - this.nativeDraggable = options.forceFallback ? false : supportDraggable; - - // Bind events - _on(el, 'mousedown', this._onTapStart); - _on(el, 'touchstart', this._onTapStart); - _on(el, 'pointerdown', this._onTapStart); - - if (this.nativeDraggable) { - _on(el, 'dragover', this); - _on(el, 'dragenter', this); - } - - touchDragOverListeners.push(this._onDragOver); - - // Restore sorting - options.store && this.sort(options.store.get(this)); - } - - - Sortable.prototype = /** @lends Sortable.prototype */ { - constructor: Sortable, - - _onTapStart: function (/** Event|TouchEvent */evt) { - var _this = this, - el = this.el, - options = this.options, - preventOnFilter = options.preventOnFilter, - type = evt.type, - touch = evt.touches && evt.touches[0], - target = (touch || evt).target, - originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0]) || target, - filter = options.filter, - startIndex; - - _saveInputCheckedState(el); - - - // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group. - if (dragEl) { - return; - } - - if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) { - return; // only left button or enabled - } - - - target = _closest(target, options.draggable, el); - - if (!target) { - return; - } - - if (lastDownEl === target) { - // Ignoring duplicate `down` - return; - } - - // Get the index of the dragged element within its parent - startIndex = _index(target, options.draggable); - - // Check filter - if (typeof filter === 'function') { - if (filter.call(this, evt, target, this)) { - _dispatchEvent(_this, originalTarget, 'filter', target, el, startIndex); - preventOnFilter && evt.preventDefault(); - return; // cancel dnd - } - } - else if (filter) { - filter = filter.split(',').some(function (criteria) { - criteria = _closest(originalTarget, criteria.trim(), el); - - if (criteria) { - _dispatchEvent(_this, criteria, 'filter', target, el, startIndex); - return true; - } - }); - - if (filter) { - preventOnFilter && evt.preventDefault(); - return; // cancel dnd - } - } - - if (options.handle && !_closest(originalTarget, options.handle, el)) { - return; - } - - // Prepare `dragstart` - this._prepareDragStart(evt, touch, target, startIndex); - }, - - _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) { - var _this = this, - el = _this.el, - options = _this.options, - ownerDocument = el.ownerDocument, - dragStartFn; - - if (target && !dragEl && (target.parentNode === el)) { - tapEvt = evt; - - rootEl = el; - dragEl = target; - parentEl = dragEl.parentNode; - nextEl = dragEl.nextSibling; - lastDownEl = target; - activeGroup = options.group; - oldIndex = startIndex; - - this._lastX = (touch || evt).clientX; - this._lastY = (touch || evt).clientY; - - dragEl.style['will-change'] = 'transform'; - - dragStartFn = function () { - // Delayed drag has been triggered - // we can re-enable the events: touchmove/mousemove - _this._disableDelayedDrag(); - - // Make the element draggable - dragEl.draggable = _this.nativeDraggable; - - // Chosen item - _toggleClass(dragEl, options.chosenClass, true); - - // Bind the events: dragstart/dragend - _this._triggerDragStart(evt, touch); - - // Drag start event - _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex); - }; - - // Disable "draggable" - options.ignore.split(',').forEach(function (criteria) { - _find(dragEl, criteria.trim(), _disableDraggable); - }); - - _on(ownerDocument, 'mouseup', _this._onDrop); - _on(ownerDocument, 'touchend', _this._onDrop); - _on(ownerDocument, 'touchcancel', _this._onDrop); - _on(ownerDocument, 'pointercancel', _this._onDrop); - _on(ownerDocument, 'selectstart', _this); - - if (options.delay) { - // If the user moves the pointer or let go the click or touch - // before the delay has been reached: - // disable the delayed drag - _on(ownerDocument, 'mouseup', _this._disableDelayedDrag); - _on(ownerDocument, 'touchend', _this._disableDelayedDrag); - _on(ownerDocument, 'touchcancel', _this._disableDelayedDrag); - _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); - _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); - _on(ownerDocument, 'pointermove', _this._disableDelayedDrag); - - _this._dragStartTimer = setTimeout(dragStartFn, options.delay); - } else { - dragStartFn(); - } - - - } - }, - - _disableDelayedDrag: function () { - var ownerDocument = this.el.ownerDocument; - - clearTimeout(this._dragStartTimer); - _off(ownerDocument, 'mouseup', this._disableDelayedDrag); - _off(ownerDocument, 'touchend', this._disableDelayedDrag); - _off(ownerDocument, 'touchcancel', this._disableDelayedDrag); - _off(ownerDocument, 'mousemove', this._disableDelayedDrag); - _off(ownerDocument, 'touchmove', this._disableDelayedDrag); - _off(ownerDocument, 'pointermove', this._disableDelayedDrag); - }, - - _triggerDragStart: function (/** Event */evt, /** Touch */touch) { - touch = touch || (evt.pointerType == 'touch' ? evt : null); - - if (touch) { - // Touch device support - tapEvt = { - target: dragEl, - clientX: touch.clientX, - clientY: touch.clientY - }; - - this._onDragStart(tapEvt, 'touch'); - } - else if (!this.nativeDraggable) { - this._onDragStart(tapEvt, true); - } - else { - _on(dragEl, 'dragend', this); - _on(rootEl, 'dragstart', this._onDragStart); - } - - try { - if (document.selection) { - // Timeout neccessary for IE9 - setTimeout(function () { - document.selection.empty(); - }); - } else { - window.getSelection().removeAllRanges(); - } - } catch (err) { - } - }, - - _dragStarted: function () { - if (rootEl && dragEl) { - var options = this.options; - - // Apply effect - _toggleClass(dragEl, options.ghostClass, true); - _toggleClass(dragEl, options.dragClass, false); - - Sortable.active = this; - - // Drag start event - _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex); - } else { - this._nulling(); - } - }, - - _emulateDragOver: function () { - if (touchEvt) { - if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) { - return; - } - - this._lastX = touchEvt.clientX; - this._lastY = touchEvt.clientY; - - if (!supportCssPointerEvents) { - _css(ghostEl, 'display', 'none'); - } - - var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY), - parent = target, - i = touchDragOverListeners.length; - - if (parent) { - do { - if (parent[expando]) { - while (i--) { - touchDragOverListeners[i]({ - clientX: touchEvt.clientX, - clientY: touchEvt.clientY, - target: target, - rootEl: parent - }); - } - - break; - } - - target = parent; // store last element - } - /* jshint boss:true */ - while (parent = parent.parentNode); - } - - if (!supportCssPointerEvents) { - _css(ghostEl, 'display', ''); - } - } - }, - - - _onTouchMove: function (/**TouchEvent*/evt) { - if (tapEvt) { - var options = this.options, - fallbackTolerance = options.fallbackTolerance, - fallbackOffset = options.fallbackOffset, - touch = evt.touches ? evt.touches[0] : evt, - dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x, - dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y, - translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; - - // only set the status to dragging, when we are actually dragging - if (!Sortable.active) { - if (fallbackTolerance && - min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance - ) { - return; - } - - this._dragStarted(); - } - - // as well as creating the ghost element on the document body - this._appendGhost(); - - moved = true; - touchEvt = touch; - - _css(ghostEl, 'webkitTransform', translate3d); - _css(ghostEl, 'mozTransform', translate3d); - _css(ghostEl, 'msTransform', translate3d); - _css(ghostEl, 'transform', translate3d); - - evt.preventDefault(); - } - }, - - _appendGhost: function () { - if (!ghostEl) { - var rect = dragEl.getBoundingClientRect(), - css = _css(dragEl), - options = this.options, - ghostRect; - - ghostEl = dragEl.cloneNode(true); - - _toggleClass(ghostEl, options.ghostClass, false); - _toggleClass(ghostEl, options.fallbackClass, true); - _toggleClass(ghostEl, options.dragClass, true); - - _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10)); - _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10)); - _css(ghostEl, 'width', rect.width); - _css(ghostEl, 'height', rect.height); - _css(ghostEl, 'opacity', '0.8'); - _css(ghostEl, 'position', 'fixed'); - _css(ghostEl, 'zIndex', '100000'); - _css(ghostEl, 'pointerEvents', 'none'); - - options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl); - - // Fixing dimensions. - ghostRect = ghostEl.getBoundingClientRect(); - _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); - _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); - } - }, - - _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { - var dataTransfer = evt.dataTransfer, - options = this.options; - - this._offUpEvents(); - - if (activeGroup.checkPull(this, this, dragEl, evt)) { - cloneEl = _clone(dragEl); - - cloneEl.draggable = false; - cloneEl.style['will-change'] = ''; - - _css(cloneEl, 'display', 'none'); - _toggleClass(cloneEl, this.options.chosenClass, false); - - rootEl.insertBefore(cloneEl, dragEl); - _dispatchEvent(this, rootEl, 'clone', dragEl); - } - - _toggleClass(dragEl, options.dragClass, true); - - if (useFallback) { - if (useFallback === 'touch') { - // Bind touch events - _on(document, 'touchmove', this._onTouchMove); - _on(document, 'touchend', this._onDrop); - _on(document, 'touchcancel', this._onDrop); - _on(document, 'pointermove', this._onTouchMove); - _on(document, 'pointerup', this._onDrop); - } else { - // Old brwoser - _on(document, 'mousemove', this._onTouchMove); - _on(document, 'mouseup', this._onDrop); - } - - this._loopId = setInterval(this._emulateDragOver, 50); - } - else { - if (dataTransfer) { - dataTransfer.effectAllowed = 'move'; - options.setData && options.setData.call(this, dataTransfer, dragEl); - } - - _on(document, 'drop', this); - setTimeout(this._dragStarted, 0); - } - }, - - _onDragOver: function (/**Event*/evt) { - var el = this.el, - target, - dragRect, - targetRect, - revert, - options = this.options, - group = options.group, - activeSortable = Sortable.active, - isOwner = (activeGroup === group), - isMovingBetweenSortable = false, - canSort = options.sort; - - if (evt.preventDefault !== void 0) { - evt.preventDefault(); - !options.dragoverBubble && evt.stopPropagation(); - } - - if (dragEl.animated) { - return; - } - - moved = true; - - if (activeSortable && !options.disabled && - (isOwner - ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list - : ( - putSortable === this || - ( - (activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) && - group.checkPut(this, activeSortable, dragEl, evt) - ) - ) - ) && - (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback - ) { - // Smart auto-scrolling - _autoScroll(evt, options, this.el); - - if (_silent) { - return; - } - - target = _closest(evt.target, options.draggable, el); - dragRect = dragEl.getBoundingClientRect(); - - if (putSortable !== this) { - putSortable = this; - isMovingBetweenSortable = true; - } - - if (revert) { - _cloneHide(activeSortable, true); - parentEl = rootEl; // actualization - - if (cloneEl || nextEl) { - rootEl.insertBefore(dragEl, cloneEl || nextEl); - } - else if (!canSort) { - rootEl.appendChild(dragEl); - } - - return; - } - - - if ((el.children.length === 0) || (el.children[0] === ghostEl) || - (el === evt.target) && (_ghostIsLast(el, evt)) - ) { - //assign target only if condition is true - if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) { - target = el.lastElementChild; - } - - if (target) { - if (target.animated) { - return; - } - - targetRect = target.getBoundingClientRect(); - } - - _cloneHide(activeSortable, isOwner); - - if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) { - if (!dragEl.contains(el)) { - el.appendChild(dragEl); - parentEl = el; // actualization - } - - this._animate(dragRect, dragEl); - target && this._animate(targetRect, target); - } - } - else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) { - if (lastEl !== target) { - lastEl = target; - lastCSS = _css(target); - lastParentCSS = _css(target.parentNode); - } - - targetRect = target.getBoundingClientRect(); - - var width = targetRect.right - targetRect.left, - height = targetRect.bottom - targetRect.top, - floating = R_FLOAT.test(lastCSS.cssFloat + lastCSS.display) - || (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0), - isWide = (target.offsetWidth > dragEl.offsetWidth), - isLong = (target.offsetHeight > dragEl.offsetHeight), - halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5, - nextSibling = target.nextElementSibling, - after = false - ; - - if (floating) { - var elTop = dragEl.offsetTop, - tgTop = target.offsetTop; - - if (elTop === tgTop) { - after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide; - } - else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) { - after = (evt.clientY - targetRect.top) / height > 0.5; - } else { - after = tgTop > elTop; - } - } else if (!isMovingBetweenSortable) { - after = (nextSibling !== dragEl) && !isLong || halfway && isLong; - } - - var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after); - - if (moveVector !== false) { - if (moveVector === 1 || moveVector === -1) { - after = (moveVector === 1); - } - - _silent = true; - setTimeout(_unsilent, 30); - - _cloneHide(activeSortable, isOwner); - - if (!dragEl.contains(el)) { - if (after && !nextSibling) { - el.appendChild(dragEl); - } else { - target.parentNode.insertBefore(dragEl, after ? nextSibling : target); - } - } - - parentEl = dragEl.parentNode; // actualization - - this._animate(dragRect, dragEl); - this._animate(targetRect, target); - } - } - } - }, - - _animate: function (prevRect, target) { - var ms = this.options.animation; - - if (ms) { - var currentRect = target.getBoundingClientRect(); - - if (prevRect.nodeType === 1) { - prevRect = prevRect.getBoundingClientRect(); - } - - _css(target, 'transition', 'none'); - _css(target, 'transform', 'translate3d(' - + (prevRect.left - currentRect.left) + 'px,' - + (prevRect.top - currentRect.top) + 'px,0)' - ); - - target.offsetWidth; // repaint - - _css(target, 'transition', 'all ' + ms + 'ms'); - _css(target, 'transform', 'translate3d(0,0,0)'); - - clearTimeout(target.animated); - target.animated = setTimeout(function () { - _css(target, 'transition', ''); - _css(target, 'transform', ''); - target.animated = false; - }, ms); - } - }, - - _offUpEvents: function () { - var ownerDocument = this.el.ownerDocument; - - _off(document, 'touchmove', this._onTouchMove); - _off(document, 'pointermove', this._onTouchMove); - _off(ownerDocument, 'mouseup', this._onDrop); - _off(ownerDocument, 'touchend', this._onDrop); - _off(ownerDocument, 'pointerup', this._onDrop); - _off(ownerDocument, 'touchcancel', this._onDrop); - _off(ownerDocument, 'pointercancel', this._onDrop); - _off(ownerDocument, 'selectstart', this); - }, - - _onDrop: function (/**Event*/evt) { - var el = this.el, - options = this.options; - - clearInterval(this._loopId); - clearInterval(autoScroll.pid); - clearTimeout(this._dragStartTimer); - - // Unbind events - _off(document, 'mousemove', this._onTouchMove); - - if (this.nativeDraggable) { - _off(document, 'drop', this); - _off(el, 'dragstart', this._onDragStart); - } - - this._offUpEvents(); - - if (evt) { - if (moved) { - evt.preventDefault(); - !options.dropBubble && evt.stopPropagation(); - } - - ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl); - - if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') { - // Remove clone - cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl); - } - - if (dragEl) { - if (this.nativeDraggable) { - _off(dragEl, 'dragend', this); - } - - _disableDraggable(dragEl); - dragEl.style['will-change'] = ''; - - // Remove class's - _toggleClass(dragEl, this.options.ghostClass, false); - _toggleClass(dragEl, this.options.chosenClass, false); - - // Drag stop event - _dispatchEvent(this, rootEl, 'unchoose', dragEl, rootEl, oldIndex); - - if (rootEl !== parentEl) { - newIndex = _index(dragEl, options.draggable); - - if (newIndex >= 0) { - // Add event - _dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex); - - // Remove event - _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); - - // drag from one list and drop into another - _dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex); - _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); - } - } - else { - if (dragEl.nextSibling !== nextEl) { - // Get the index of the dragged element within its parent - newIndex = _index(dragEl, options.draggable); - - if (newIndex >= 0) { - // drag & drop within the same list - _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); - _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); - } - } - } - - if (Sortable.active) { - /* jshint eqnull:true */ - if (newIndex == null || newIndex === -1) { - newIndex = oldIndex; - } - - _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); - - // Save sorting - this.save(); - } - } - - } - - this._nulling(); - }, - - _nulling: function() { - rootEl = - dragEl = - parentEl = - ghostEl = - nextEl = - cloneEl = - lastDownEl = - - scrollEl = - scrollParentEl = - - tapEvt = - touchEvt = - - moved = - newIndex = - - lastEl = - lastCSS = - - putSortable = - activeGroup = - Sortable.active = null; - - savedInputChecked.forEach(function (el) { - el.checked = true; - }); - savedInputChecked.length = 0; - }, - - handleEvent: function (/**Event*/evt) { - switch (evt.type) { - case 'drop': - case 'dragend': - this._onDrop(evt); - break; - - case 'dragover': - case 'dragenter': - if (dragEl) { - this._onDragOver(evt); - _globalDragOver(evt); - } - break; - - case 'selectstart': - evt.preventDefault(); - break; - } - }, - - - /** - * Serializes the item into an array of string. - * @returns {String[]} - */ - toArray: function () { - var order = [], - el, - children = this.el.children, - i = 0, - n = children.length, - options = this.options; - - for (; i < n; i++) { - el = children[i]; - if (_closest(el, options.draggable, this.el)) { - order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); - } - } - - return order; - }, - - - /** - * Sorts the elements according to the array. - * @param {String[]} order order of the items - */ - sort: function (order) { - var items = {}, rootEl = this.el; - - this.toArray().forEach(function (id, i) { - var el = rootEl.children[i]; - - if (_closest(el, this.options.draggable, rootEl)) { - items[id] = el; - } - }, this); - - order.forEach(function (id) { - if (items[id]) { - rootEl.removeChild(items[id]); - rootEl.appendChild(items[id]); - } - }); - }, - - - /** - * Save the current sorting - */ - save: function () { - var store = this.options.store; - store && store.set(this); - }, - - - /** - * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. - * @param {HTMLElement} el - * @param {String} [selector] default: `options.draggable` - * @returns {HTMLElement|null} - */ - closest: function (el, selector) { - return _closest(el, selector || this.options.draggable, this.el); - }, - - - /** - * Set/get option - * @param {string} name - * @param {*} [value] - * @returns {*} - */ - option: function (name, value) { - var options = this.options; - - if (value === void 0) { - return options[name]; - } else { - options[name] = value; - - if (name === 'group') { - _prepareGroup(options); - } - } - }, - - - /** - * Destroy - */ - destroy: function () { - var el = this.el; - - el[expando] = null; - - _off(el, 'mousedown', this._onTapStart); - _off(el, 'touchstart', this._onTapStart); - _off(el, 'pointerdown', this._onTapStart); - - if (this.nativeDraggable) { - _off(el, 'dragover', this); - _off(el, 'dragenter', this); - } - - // Remove draggable attributes - Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { - el.removeAttribute('draggable'); - }); - - touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1); - - this._onDrop(); - - this.el = el = null; - } - }; - - - function _cloneHide(sortable, state) { - if (sortable.lastPullMode !== 'clone') { - state = true; - } - - if (cloneEl && (cloneEl.state !== state)) { - _css(cloneEl, 'display', state ? 'none' : ''); - - if (!state) { - if (cloneEl.state) { - if (sortable.options.group.revertClone) { - rootEl.insertBefore(cloneEl, nextEl); - sortable._animate(dragEl, cloneEl); - } else { - rootEl.insertBefore(cloneEl, dragEl); - } - } - } - - cloneEl.state = state; - } - } - - - function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) { - if (el) { - ctx = ctx || document; - - do { - if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) { - return el; - } - /* jshint boss:true */ - } while (el = _getParentOrHost(el)); - } - - return null; - } - - - function _getParentOrHost(el) { - var parent = el.host; - - return (parent && parent.nodeType) ? parent : el.parentNode; - } - - - function _globalDragOver(/**Event*/evt) { - if (evt.dataTransfer) { - evt.dataTransfer.dropEffect = 'move'; - } - evt.preventDefault(); - } - - - function _on(el, event, fn) { - el.addEventListener(event, fn, captureMode); - } - - - function _off(el, event, fn) { - el.removeEventListener(event, fn, captureMode); - } - - - function _toggleClass(el, name, state) { - if (el) { - if (el.classList) { - el.classList[state ? 'add' : 'remove'](name); - } - else { - var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); - el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); - } - } - } - - - function _css(el, prop, val) { - var style = el && el.style; - - if (style) { - if (val === void 0) { - if (document.defaultView && document.defaultView.getComputedStyle) { - val = document.defaultView.getComputedStyle(el, ''); - } - else if (el.currentStyle) { - val = el.currentStyle; - } - - return prop === void 0 ? val : val[prop]; - } - else { - if (!(prop in style)) { - prop = '-webkit-' + prop; - } - - style[prop] = val + (typeof val === 'string' ? '' : 'px'); - } - } - } - - - function _find(ctx, tagName, iterator) { - if (ctx) { - var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length; - - if (iterator) { - for (; i < n; i++) { - iterator(list[i], i); - } - } - - return list; - } - - return []; - } - - - - function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) { - sortable = (sortable || rootEl[expando]); - - var evt = document.createEvent('Event'), - options = sortable.options, - onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); - - evt.initEvent(name, true, true); - - evt.to = rootEl; - evt.from = fromEl || rootEl; - evt.item = targetEl || rootEl; - evt.clone = cloneEl; - - evt.oldIndex = startIndex; - evt.newIndex = newIndex; - - rootEl.dispatchEvent(evt); - - if (options[onName]) { - options[onName].call(sortable, evt); - } - } - - - function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) { - var evt, - sortable = fromEl[expando], - onMoveFn = sortable.options.onMove, - retVal; - - evt = document.createEvent('Event'); - evt.initEvent('move', true, true); - - evt.to = toEl; - evt.from = fromEl; - evt.dragged = dragEl; - evt.draggedRect = dragRect; - evt.related = targetEl || toEl; - evt.relatedRect = targetRect || toEl.getBoundingClientRect(); - evt.willInsertAfter = willInsertAfter; - - fromEl.dispatchEvent(evt); - - if (onMoveFn) { - retVal = onMoveFn.call(sortable, evt, originalEvt); - } - - return retVal; - } - - - function _disableDraggable(el) { - el.draggable = false; - } - - - function _unsilent() { - _silent = false; - } - - - /** @returns {HTMLElement|false} */ - function _ghostIsLast(el, evt) { - var lastEl = el.lastElementChild, - rect = lastEl.getBoundingClientRect(); - - // 5 — min delta - // abs — нельзя добавлять, а то глюки при наведении сверху - return (evt.clientY - (rect.top + rect.height) > 5) || - (evt.clientX - (rect.left + rect.width) > 5); - } - - - /** - * Generate id - * @param {HTMLElement} el - * @returns {String} - * @private - */ - function _generateId(el) { - var str = el.tagName + el.className + el.src + el.href + el.textContent, - i = str.length, - sum = 0; - - while (i--) { - sum += str.charCodeAt(i); - } - - return sum.toString(36); - } - - /** - * Returns the index of an element within its parent for a selected set of - * elements - * @param {HTMLElement} el - * @param {selector} selector - * @return {number} - */ - function _index(el, selector) { - var index = 0; - - if (!el || !el.parentNode) { - return -1; - } - - while (el && (el = el.previousElementSibling)) { - if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) { - index++; - } - } - - return index; - } - - function _matches(/**HTMLElement*/el, /**String*/selector) { - if (el) { - selector = selector.split('.'); - - var tag = selector.shift().toUpperCase(), - re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g'); - - return ( - (tag === '' || el.nodeName.toUpperCase() == tag) && - (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length) - ); - } - - return false; - } - - function _throttle(callback, ms) { - var args, _this; - - return function () { - if (args === void 0) { - args = arguments; - _this = this; - - setTimeout(function () { - if (args.length === 1) { - callback.call(_this, args[0]); - } else { - callback.apply(_this, args); - } - - args = void 0; - }, ms); - } - }; - } - - function _extend(dst, src) { - if (dst && src) { - for (var key in src) { - if (src.hasOwnProperty(key)) { - dst[key] = src[key]; - } - } - } - - return dst; - } - - function _clone(el) { - return $ - ? $(el).clone(true)[0] - : (Polymer && Polymer.dom - ? Polymer.dom(el).cloneNode(true) - : el.cloneNode(true) - ); - } - - function _saveInputCheckedState(root) { - var inputs = root.getElementsByTagName('input'); - var idx = inputs.length; - - while (idx--) { - var el = inputs[idx]; - el.checked && savedInputChecked.push(el); - } - } - - // Fixed #973: - _on(document, 'touchmove', function (evt) { - if (Sortable.active) { - evt.preventDefault(); - } - }); - - try { - window.addEventListener('test', null, Object.defineProperty({}, 'passive', { - get: function () { - captureMode = { - capture: false, - passive: false - }; - } - })); - } catch (err) {} - - // Export utils - Sortable.utils = { - on: _on, - off: _off, - css: _css, - find: _find, - is: function (el, selector) { - return !!_closest(el, selector, el); - }, - extend: _extend, - throttle: _throttle, - closest: _closest, - toggleClass: _toggleClass, - clone: _clone, - index: _index - }; - - - /** - * Create sortable instance - * @param {HTMLElement} el - * @param {Object} [options] - */ - Sortable.create = function (el, options) { - return new Sortable(el, options); - }; - - - // Export - Sortable.version = '1.6.1'; - return Sortable; -}); - -},{}],47:[function(require,module,exports){ -(function (setImmediate,clearImmediate){ -var nextTick = require('process/browser.js').nextTick; -var apply = Function.prototype.apply; -var slice = Array.prototype.slice; -var immediateIds = {}; -var nextImmediateId = 0; - -// DOM APIs, for completeness - -exports.setTimeout = function() { - return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); -}; -exports.setInterval = function() { - return new Timeout(apply.call(setInterval, window, arguments), clearInterval); -}; -exports.clearTimeout = -exports.clearInterval = function(timeout) { timeout.close(); }; - -function Timeout(id, clearFn) { - this._id = id; - this._clearFn = clearFn; -} -Timeout.prototype.unref = Timeout.prototype.ref = function() {}; -Timeout.prototype.close = function() { - this._clearFn.call(window, this._id); -}; - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = msecs; -}; - -exports.unenroll = function(item) { - clearTimeout(item._idleTimeoutId); - item._idleTimeout = -1; -}; - -exports._unrefActive = exports.active = function(item) { - clearTimeout(item._idleTimeoutId); - - var msecs = item._idleTimeout; - if (msecs >= 0) { - item._idleTimeoutId = setTimeout(function onTimeout() { - if (item._onTimeout) - item._onTimeout(); - }, msecs); - } -}; - -// That's not how node.js implements it but the exposed api is the same. -exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { - var id = nextImmediateId++; - var args = arguments.length < 2 ? false : slice.call(arguments, 1); - - immediateIds[id] = true; - - nextTick(function onNextTick() { - if (immediateIds[id]) { - // fn.call() is faster so we optimize for the common use-case - // @see http://jsperf.com/call-apply-segu - if (args) { - fn.apply(null, args); - } else { - fn.call(null); - } - // Prevent ids from leaking - exports.clearImmediate(id); - } - }); - - return id; -}; - -exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { - delete immediateIds[id]; -}; -}).call(this,require("timers").setImmediate,require("timers").clearImmediate) -},{"process/browser.js":45,"timers":47}],48:[function(require,module,exports){ -var Vue // late bind -var version -var map = (window.__VUE_HOT_MAP__ = Object.create(null)) -var installed = false -var isBrowserify = false -var initHookName = 'beforeCreate' - -exports.install = function (vue, browserify) { - if (installed) { return } - installed = true - - Vue = vue.__esModule ? vue.default : vue - version = Vue.version.split('.').map(Number) - isBrowserify = browserify - - // compat with < 2.0.0-alpha.7 - if (Vue.config._lifecycleHooks.indexOf('init') > -1) { - initHookName = 'init' - } - - exports.compatible = version[0] >= 2 - if (!exports.compatible) { - console.warn( - '[HMR] You are using a version of vue-hot-reload-api that is ' + - 'only compatible with Vue.js core ^2.0.0.' - ) - return - } -} - -/** - * Create a record for a hot module, which keeps track of its constructor - * and instances - * - * @param {String} id - * @param {Object} options - */ - -exports.createRecord = function (id, options) { - var Ctor = null - if (typeof options === 'function') { - Ctor = options - options = Ctor.options - } - makeOptionsHot(id, options) - map[id] = { - Ctor: Ctor, - options: options, - instances: [] - } -} - -/** - * Make a Component options object hot. - * - * @param {String} id - * @param {Object} options - */ - -function makeOptionsHot(id, options) { - if (options.functional) { - var render = options.render - options.render = function (h, ctx) { - var instances = map[id].instances - if (instances.indexOf(ctx.parent) < 0) { - instances.push(ctx.parent) - } - return render(h, ctx) - } - } else { - injectHook(options, initHookName, function() { - var record = map[id] - if (!record.Ctor) { - record.Ctor = this.constructor - } - record.instances.push(this) - }) - injectHook(options, 'beforeDestroy', function() { - var instances = map[id].instances - instances.splice(instances.indexOf(this), 1) - }) - } -} - -/** - * Inject a hook to a hot reloadable component so that - * we can keep track of it. - * - * @param {Object} options - * @param {String} name - * @param {Function} hook - */ - -function injectHook(options, name, hook) { - var existing = options[name] - options[name] = existing - ? Array.isArray(existing) ? existing.concat(hook) : [existing, hook] - : [hook] -} - -function tryWrap(fn) { - return function (id, arg) { - try { - fn(id, arg) - } catch (e) { - console.error(e) - console.warn( - 'Something went wrong during Vue component hot-reload. Full reload required.' - ) - } - } -} - -function updateOptions (oldOptions, newOptions) { - for (var key in oldOptions) { - if (!(key in newOptions)) { - delete oldOptions[key] - } - } - for (var key$1 in newOptions) { - oldOptions[key$1] = newOptions[key$1] - } -} - -exports.rerender = tryWrap(function (id, options) { - var record = map[id] - if (!options) { - record.instances.slice().forEach(function (instance) { - instance.$forceUpdate() - }) - return - } - if (typeof options === 'function') { - options = options.options - } - if (record.Ctor) { - record.Ctor.options.render = options.render - record.Ctor.options.staticRenderFns = options.staticRenderFns - record.instances.slice().forEach(function (instance) { - instance.$options.render = options.render - instance.$options.staticRenderFns = options.staticRenderFns - // reset static trees - if (instance._staticTrees) { - // pre 2.5 staticTrees are cached per-instance - instance._staticTrees = [] - } else { - // post 2.5 staticTrees are cached on shared options - record.Ctor.options._staticTrees = [] - } - instance.$forceUpdate() - }) - } else { - // functional or no instance created yet - record.options.render = options.render - record.options.staticRenderFns = options.staticRenderFns - - // handle functional component re-render - if (record.options.functional) { - // rerender with full options - if (Object.keys(options).length > 2) { - updateOptions(record.options, options) - } else { - // template-only rerender. - // need to inject the style injection code for CSS modules - // to work properly. - var injectStyles = record.options._injectStyles - if (injectStyles) { - var render = options.render - record.options.render = function (h, ctx) { - injectStyles.call(ctx) - return render(h, ctx) - } - } - } - record.options._Ctor = null - record.options._staticTrees = [] - record.instances.slice().forEach(function (instance) { - instance.$forceUpdate() - }) - } - } -}) - -exports.reload = tryWrap(function (id, options) { - var record = map[id] - if (options) { - if (typeof options === 'function') { - options = options.options - } - makeOptionsHot(id, options) - if (record.Ctor) { - if (version[1] < 2) { - // preserve pre 2.2 behavior for global mixin handling - record.Ctor.extendOptions = options - } - var newCtor = record.Ctor.super.extend(options) - record.Ctor.options = newCtor.options - record.Ctor.cid = newCtor.cid - record.Ctor.prototype = newCtor.prototype - if (newCtor.release) { - // temporary global mixin strategy used in < 2.0.0-alpha.6 - newCtor.release() - } - } else { - updateOptions(record.options, options) - } - } - record.instances.slice().forEach(function (instance) { - if (instance.$vnode && instance.$vnode.context) { - instance.$vnode.context.$forceUpdate() - } else { - console.warn( - 'Root or manually mounted instance modified. Full reload required.' - ) - } - }) -}) - -},{}],49:[function(require,module,exports){ -/*! - * vue-resource v1.3.4 - * https://github.com/pagekit/vue-resource - * Released under the MIT License. - */ - -'use strict'; - -/** - * Promises/A+ polyfill v1.1.4 (https://github.com/bramstein/promis) - */ - -var RESOLVED = 0; -var REJECTED = 1; -var PENDING = 2; - -function Promise$1(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$1.reject = function (r) { - return new Promise$1(function (resolve, reject) { - reject(r); - }); -}; - -Promise$1.resolve = function (x) { - return new Promise$1(function (resolve, reject) { - resolve(x); - }); -}; - -Promise$1.all = function all(iterable) { - return new Promise$1(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$1.resolve(iterable[i]).then(resolver(i), reject); - } - }); -}; - -Promise$1.race = function race(iterable) { - return new Promise$1(function (resolve, reject) { - for (var i = 0; i < iterable.length; i += 1) { - Promise$1.resolve(iterable[i]).then(resolve, reject); - } - }); -}; - -var p$1 = Promise$1.prototype; - -p$1.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$1.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$1.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$1.then = function then(onResolved, onRejected) { - var promise = this; - - return new Promise$1(function (resolve, reject) { - promise.deferred.push([onResolved, onRejected, resolve, reject]); - promise.notify(); - }); -}; - -p$1.catch = function (onRejected) { - return this.then(undefined, onRejected); -}; - -/** - * Promise adapter. - */ - -if (typeof Promise === 'undefined') { - window.Promise = Promise$1; -} - -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); - } - ); -}; - -/** - * Utility functions. - */ - -var ref = {}; -var hasOwnProperty = ref.hasOwnProperty; - -var ref$1 = []; -var slice = ref$1.slice; -var debug = false; -var ntick; - -var inBrowser = typeof window !== 'undefined'; - -var Util = function (ref) { - var config = ref.config; - var nextTick = ref.nextTick; - - ntick = nextTick; - debug = config.debug || !config.silent; -}; - -function warn(msg) { - if (typeof console !== 'undefined' && debug) { - console.warn('[VueResource warn]: ' + msg); - } -} - -function error(msg) { - if (typeof console !== 'undefined') { - console.error(msg); - } -} - -function nextTick(cb, ctx) { - return ntick(cb, ctx); -} - -function trim(str) { - return str ? str.replace(/^\s*|\s*$/g, '') : ''; -} - -function trimEnd(str, chars) { - - if (str && chars === undefined) { - return str.replace(/\s+$/, ''); - } - - if (!str || !chars) { - return str; - } - - return str.replace(new RegExp(("[" + chars + "]+$")), ''); -} - -function toLower(str) { - return str ? str.toLowerCase() : ''; -} - -function toUpper(str) { - return str ? str.toUpperCase() : ''; -} - -var isArray = Array.isArray; - -function isString(val) { - return typeof val === 'string'; -} - - - -function isFunction(val) { - return typeof val === 'function'; -} - -function isObject(obj) { - return obj !== null && typeof obj === 'object'; -} - -function isPlainObject(obj) { - return isObject(obj) && Object.getPrototypeOf(obj) == Object.prototype; -} - -function isBlob(obj) { - return typeof Blob !== 'undefined' && obj instanceof Blob; -} - -function isFormData(obj) { - return typeof FormData !== 'undefined' && obj instanceof FormData; -} - -function when(value, fulfilled, rejected) { - - var promise = PromiseObj.resolve(value); - - if (arguments.length < 2) { - return promise; - } - - return promise.then(fulfilled, rejected); -} - -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}); -} - -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; -} - -var assign = Object.assign || _assign; - -function merge(target) { - - var args = slice.call(arguments, 1); - - args.forEach(function (source) { - _merge(target, source, true); - }); - - return target; -} - -function defaults(target) { - - var args = slice.call(arguments, 1); - - args.forEach(function (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(function (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]; - } - } -} - -/** - * Root Prefix Transform. - */ - -var root = function (options$$1, next) { - - var url = next(options$$1); - - if (isString(options$$1.root) && !/^(https?:)?\//.test(url)) { - url = trimEnd(options$$1.root, '/') + '/' + url; - } - - return url; -}; - -/** - * Query Parameter Transform. - */ - -var query = function (options$$1, next) { - - var urlParams = Object.keys(Url.options.params), query = {}, url = next(options$$1); - - each(options$$1.params, function (value, key) { - if (urlParams.indexOf(key) === -1) { - query[key] = value; - } - }); - - query = Url.params(query); - - if (query) { - url += (url.indexOf('?') == -1 ? '?' : '&') + query; - } - - return url; -}; - -/** - * URL Template v2.0.6 (https://github.com/bramstein/url-template) - */ - -function expand(url, params, variables) { - - var tmpl = parse(url), expanded = tmpl.expand(params); - - if (variables) { - variables.push.apply(variables, tmpl.vars); - } - - return expanded; -} - -function parse(template) { - - var operators = ['+', '#', '.', '/', ';', '?', '&'], variables = []; - - return { - vars: variables, - expand: function expand(context) { - return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, 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(function (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(function (value) { - result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null)); - }); - } else { - Object.keys(value).forEach(function (k) { - if (isDefined(value[k])) { - result.push(encodeValue(operator, value[k], k)); - } - }); - } - } else { - var tmp = []; - - if (Array.isArray(value)) { - value.filter(isDefined).forEach(function (value) { - tmp.push(encodeValue(operator, value)); - }); - } else { - Object.keys(value).forEach(function (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(function (part) { - if (!/%[0-9A-Fa-f]/.test(part)) { - part = encodeURI(part); - } - return part; - }).join(''); -} - -/** - * URL Template (RFC 6570) Transform. - */ - -var template = function (options) { - - var variables = [], url = expand(options.url, options.params, variables); - - variables.forEach(function (key) { - delete options.params[key]; - }); - - return url; -}; - -/** - * Service for URL templating. - */ - -function Url(url, params) { - - var self = this || {}, options$$1 = url, transform; - - if (isString(url)) { - options$$1 = {url: url, params: params}; - } - - options$$1 = merge({}, Url.options, self.$options, options$$1); - - Url.transforms.forEach(function (handler) { - - if (isString(handler)) { - handler = Url.transform[handler]; - } - - if (isFunction(handler)) { - transform = factory(handler, transform, self.$vm); - } - - }); - - return transform(options$$1); -} - -/** - * Url options. - */ - -Url.options = { - url: '', - root: null, - params: {} -}; - -/** - * Url transforms. - */ - -Url.transform = {template: template, query: query, root: 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 function (options$$1) { - return handler.call(vm, options$$1, next); - }; -} - -function serialize(params, obj, scope) { - - var array = isArray(obj), plain = isPlainObject(obj), hash; - - each(obj, function (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); - } - }); -} - -/** - * XDomain client (Internet Explorer). - */ - -var xdrClient = function (request) { - return new PromiseObj(function (resolve) { - - var xdr = new XDomainRequest(), handler = function (ref) { - var type = ref.type; - - - var status = 0; - - if (type === 'load') { - status = 200; - } else if (type === 'error') { - status = 500; - } - - resolve(request.respondWith(xdr.responseText, {status: status})); - }; - - request.abort = function () { return 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 = function () {}; - xdr.send(request.getBody()); - }); -}; - -/** - * CORS Interceptor. - */ - -var SUPPORTS_CORS = inBrowser && 'withCredentials' in new XMLHttpRequest(); - -var cors = function (request, next) { - - if (inBrowser) { - - var orgUrl = Url.parse(location.href); - var 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; - } - } - } - - next(); -}; - -/** - * Form data Interceptor. - */ - -var form = function (request, next) { - - 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'); - } - - next(); -}; - -/** - * JSON Interceptor. - */ - -var json = function (request, next) { - - var type = request.headers.get('Content-Type') || ''; - - if (isObject(request.body) && type.indexOf('application/json') === 0) { - request.body = JSON.stringify(request.body); - } - - next(function (response) { - - return response.bodyText ? when(response.text(), function (text) { - - 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) { - - var start = str.match(/^\[|^\{(?!\{)/), end = {'[': /]$/, '{': /}$/}; - - return start && end[start[0]].test(str); -} - -/** - * JSONP client (Browser). - */ - -var jsonpClient = function (request) { - return new PromiseObj(function (resolve) { - - var name = request.jsonp || 'callback', callback = request.jsonpCallback || '_jsonp' + Math.random().toString(36).substr(2), body = null, handler, script; - - handler = function (ref) { - var type = ref.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: status})); - }; - - window[callback] = function (result) { - body = JSON.stringify(result); - }; - - request.abort = function () { - 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); - }); -}; - -/** - * JSONP Interceptor. - */ - -var jsonp = function (request, next) { - - if (request.method == 'JSONP') { - request.client = jsonpClient; - } - - next(); -}; - -/** - * Before Interceptor. - */ - -var before = function (request, next) { - - if (isFunction(request.before)) { - request.before.call(this, request); - } - - next(); -}; - -/** - * HTTP method override Interceptor. - */ - -var method = function (request, next) { - - if (request.emulateHTTP && /^(PUT|PATCH|DELETE)$/i.test(request.method)) { - request.headers.set('X-HTTP-Method-Override', request.method); - request.method = 'POST'; - } - - next(); -}; - -/** - * Header Interceptor. - */ - -var header = function (request, next) { - - var headers = assign({}, Http.headers.common, - !request.crossOrigin ? Http.headers.custom : {}, - Http.headers[toLower(request.method)] - ); - - each(headers, function (value, name) { - if (!request.headers.has(name)) { - request.headers.set(name, value); - } - }); - - next(); -}; - -/** - * XMLHttp client (Browser). - */ - -var xhrClient = function (request) { - return new PromiseObj(function (resolve) { - - var xhr = new XMLHttpRequest(), handler = function (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'), function (row) { - response.headers.append(row.slice(0, row.indexOf(':')), row.slice(row.indexOf(':') + 1)); - }); - - resolve(response); - }; - - request.abort = function () { return xhr.abort(); }; - - if (request.progress) { - if (request.method === 'GET') { - xhr.addEventListener('progress', request.progress); - } else if (/^(POST|PUT)$/i.test(request.method)) { - xhr.upload.addEventListener('progress', request.progress); - } - } - - 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'); - } - - request.headers.forEach(function (value, name) { - xhr.setRequestHeader(name, value); - }); - - xhr.onload = handler; - xhr.onabort = handler; - xhr.onerror = handler; - xhr.ontimeout = handler; - xhr.send(request.getBody()); - }); -}; - -/** - * Http client (Node). - */ - -var nodeClient = function (request) { - - var client = require('got'); - - return new PromiseObj(function (resolve) { - - var url = request.getUrl(); - var body = request.getBody(); - var method = request.method; - var headers = {}, handler; - - request.headers.forEach(function (value, name) { - headers[name] = value; - }); - - client(url, {body: body, method: method, headers: headers}).then(handler = function (resp) { - - var response = request.respondWith(resp.body, { - status: resp.statusCode, - statusText: trim(resp.statusMessage) - } - ); - - each(resp.headers, function (value, name) { - response.headers.set(name, value); - }); - - resolve(response); - - }, function (error$$1) { return handler(error$$1.response); }); - }); -}; - -/** - * Base client. - */ - -var Client = function (context) { - - var reqHandlers = [sendRequest], resHandlers = [], handler; - - if (!isObject(context)) { - context = null; - } - - function Client(request) { - return new PromiseObj(function (resolve, reject) { - - function exec() { - - handler = reqHandlers.pop(); - - if (isFunction(handler)) { - handler.call(context, request, next); - } else { - warn(("Invalid interceptor of type " + (typeof handler) + ", must be a function")); - next(); - } - } - - function next(response) { - - if (isFunction(response)) { - - resHandlers.unshift(response); - - } else if (isObject(response)) { - - resHandlers.forEach(function (handler) { - response = when(response, function (response) { - return handler.call(context, response) || response; - }, reject); - }); - - when(response, resolve, reject); - - return; - } - - exec(); - } - - exec(); - - }, context); - } - - Client.use = function (handler) { - reqHandlers.push(handler); - }; - - return Client; -}; - -function sendRequest(request, resolve) { - - var client = request.client || (inBrowser ? xhrClient : nodeClient); - - resolve(client(request)); -} - -/** - * HTTP Headers. - */ - -var Headers = function Headers(headers) { - var this$1 = this; - - - this.map = {}; - - each(headers, function (value, name) { return this$1.append(name, value); }); -}; - -Headers.prototype.has = function has (name) { - return getName(this.map, name) !== null; -}; - -Headers.prototype.get = function get (name) { - - var list = this.map[getName(this.map, name)]; - - return list ? list.join() : null; -}; - -Headers.prototype.getAll = function getAll (name) { - return this.map[getName(this.map, name)] || []; -}; - -Headers.prototype.set = function set (name, value) { - this.map[normalizeName(getName(this.map, name) || name)] = [trim(value)]; -}; - -Headers.prototype.append = function append (name, value){ - - var list = this.map[getName(this.map, name)]; - - if (list) { - list.push(trim(value)); - } else { - this.set(name, value); - } -}; - -Headers.prototype.delete = function delete$1 (name){ - delete this.map[getName(this.map, name)]; -}; - -Headers.prototype.deleteAll = function deleteAll (){ - this.map = {}; -}; - -Headers.prototype.forEach = function forEach (callback, thisArg) { - var this$1 = this; - - each(this.map, function (list, name) { - each(list, function (value) { return callback.call(thisArg, value, name, this$1); }); - }); -}; - -function getName(map, name) { - return Object.keys(map).reduce(function (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); -} - -/** - * HTTP Response. - */ - -var Response = function Response(body, ref) { - var url = ref.url; - var headers = ref.headers; - var status = ref.status; - var statusText = ref.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); - } - } -}; - -Response.prototype.blob = function blob () { - return when(this.bodyBlob); -}; - -Response.prototype.text = function text () { - return when(this.bodyText); -}; - -Response.prototype.json = function json () { - return when(this.text(), function (text) { return JSON.parse(text); }); -}; - -Object.defineProperty(Response.prototype, 'data', { - - get: function get() { - return this.body; - }, - - set: function set(body) { - this.body = body; - } - -}); - -function blobText(body) { - return new PromiseObj(function (resolve) { - - var reader = new FileReader(); - - reader.readAsText(body); - reader.onload = function () { - resolve(reader.result); - }; - - }); -} - -function isBlobText(body) { - return body.type.indexOf('text') === 0 || body.type.indexOf('json') !== -1; -} - -/** - * HTTP Request. - */ - -var Request = function Request(options$$1) { - - this.body = null; - this.params = {}; - - assign(this, options$$1, { - method: toUpper(options$$1.method || 'GET') - }); - - if (!(this.headers instanceof Headers)) { - this.headers = new Headers(this.headers); - } -}; - -Request.prototype.getUrl = function getUrl (){ - return Url(this); -}; - -Request.prototype.getBody = function getBody (){ - return this.body; -}; - -Request.prototype.respondWith = function respondWith (body, options$$1) { - return new Response(body, assign(options$$1 || {}, {url: this.getUrl()})); -}; - -/** - * Service for sending network requests. - */ - -var COMMON_HEADERS = {'Accept': 'application/json, text/plain, */*'}; -var JSON_CONTENT_TYPE = {'Content-Type': 'application/json;charset=utf-8'}; - -function Http(options$$1) { - - var self = this || {}, client = Client(self.$vm); - - defaults(options$$1 || {}, self.$options, Http.options); - - Http.interceptors.forEach(function (handler) { - - if (isString(handler)) { - handler = Http.interceptor[handler]; - } - - if (isFunction(handler)) { - client.use(handler); - } - - }); - - return client(new Request(options$$1)).then(function (response) { - - return response.ok ? response : PromiseObj.reject(response); - - }, function (response) { - - if (response instanceof Error) { - error(response); - } - - return PromiseObj.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: before, method: method, jsonp: jsonp, json: json, form: form, header: header, cors: cors}; -Http.interceptors = ['before', 'method', 'jsonp', 'json', 'form', 'header', 'cors']; - -['get', 'delete', 'head', 'jsonp'].forEach(function (method$$1) { - - Http[method$$1] = function (url, options$$1) { - return this(assign(options$$1 || {}, {url: url, method: method$$1})); - }; - -}); - -['post', 'put', 'patch'].forEach(function (method$$1) { - - Http[method$$1] = function (url, body, options$$1) { - return this(assign(options$$1 || {}, {url: url, method: method$$1, body: body})); - }; - -}); - -/** - * Service for interacting with RESTful services. - */ - -function Resource(url, params, actions, options$$1) { - - var self = this || {}, resource = {}; - - actions = assign({}, - Resource.actions, - actions - ); - - each(actions, function (action, name) { - - action = merge({url: url, params: assign({}, params)}, options$$1, action); - - resource[name] = function () { - return (self.$http || Http)(opts(action, arguments)); - }; - }); - - return resource; -} - -function opts(action, args) { - - var options$$1 = 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$$1.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$$1.body = body; - options$$1.params = assign({}, options$$1.params, params); - - return options$$1; -} - -Resource.actions = { - - get: {method: 'GET'}, - save: {method: 'POST'}, - query: {method: 'GET'}, - update: {method: 'PUT'}, - remove: {method: 'DELETE'}, - delete: {method: 'DELETE'} - -}; - -/** - * Install plugin. - */ - -function plugin(Vue) { - - if (plugin.installed) { - return; - } - - Util(Vue); - - Vue.url = Url; - Vue.http = Http; - Vue.resource = Resource; - Vue.Promise = PromiseObj; - - Object.defineProperties(Vue.prototype, { - - $url: { - get: function get() { - return options(Vue.url, this, this.$options.url); - } - }, - - $http: { - get: function get() { - return options(Vue.http, this, this.$options.http); - } - }, - - $resource: { - get: function get() { - return Vue.resource.bind(this); - } - }, - - $promise: { - get: function get() { - var this$1 = this; - - return function (executor) { return new Vue.Promise(executor, this$1); }; - } - } - - }); -} - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(plugin); -} - -module.exports = plugin; - -},{"got":7}],50:[function(require,module,exports){ -(function (process,global,setImmediate){ -/*! - * Vue.js v2.5.2 - * (c) 2014-2017 Evan You - * Released under the MIT License. - */ -'use strict'; - -/* */ - -// these helpers produces better vm code in JS engines due to their -// explicitness and function inlining -function isUndef (v) { - return v === undefined || v === null -} - -function isDef (v) { - return v !== undefined && v !== null -} - -function isTrue (v) { - return v === true -} - -function isFalse (v) { - return v === false -} - -/** - * Check if value is primitive - */ -function isPrimitive (value) { - return ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ) -} - -/** - * Quick object check - this is primarily used to tell - * Objects from primitive values when we know the value - * is a JSON-compliant type. - */ -function isObject (obj) { - return obj !== null && typeof obj === 'object' -} - -/** - * Get the raw type string of a value e.g. [object Object] - */ -var _toString = Object.prototype.toString; - -function toRawType (value) { - return _toString.call(value).slice(8, -1) -} - -/** - * Strict object type check. Only returns true - * for plain JavaScript objects. - */ -function isPlainObject (obj) { - return _toString.call(obj) === '[object Object]' -} - -function isRegExp (v) { - return _toString.call(v) === '[object RegExp]' -} - -/** - * Check if val is a valid array index. - */ -function isValidArrayIndex (val) { - var n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) -} - -/** - * Convert a value to a string that is actually rendered. - */ -function toString (val) { - return val == null - ? '' - : typeof val === 'object' - ? JSON.stringify(val, null, 2) - : String(val) -} - -/** - * Convert a input value to a number for persistence. - * If the conversion fails, return original string. - */ -function toNumber (val) { - var n = parseFloat(val); - return isNaN(n) ? val : n -} - -/** - * Make a map and return a function for checking if a key - * is in that map. - */ -function makeMap ( - str, - expectsLowerCase -) { - var map = Object.create(null); - var list = str.split(','); - for (var i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase - ? function (val) { return map[val.toLowerCase()]; } - : function (val) { return map[val]; } -} - -/** - * Check if a tag is a built-in tag. - */ -var isBuiltInTag = makeMap('slot,component', true); - -/** - * Check if a attribute is a reserved attribute. - */ -var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); - -/** - * Remove an item from an array - */ -function remove (arr, item) { - if (arr.length) { - var index = arr.indexOf(item); - if (index > -1) { - return arr.splice(index, 1) - } - } -} - -/** - * Check whether the object has the property. - */ -var hasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn (obj, key) { - return hasOwnProperty.call(obj, key) -} - -/** - * Create a cached version of a pure function. - */ -function cached (fn) { - var cache = Object.create(null); - return (function cachedFn (str) { - var hit = cache[str]; - return hit || (cache[str] = fn(str)) - }) -} - -/** - * Camelize a hyphen-delimited string. - */ -var camelizeRE = /-(\w)/g; -var camelize = cached(function (str) { - return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) -}); - -/** - * Capitalize a string. - */ -var capitalize = cached(function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) -}); - -/** - * Hyphenate a camelCase string. - */ -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cached(function (str) { - return str.replace(hyphenateRE, '-$1').toLowerCase() -}); - -/** - * Simple bind, faster than native - */ -function bind (fn, ctx) { - function boundFn (a) { - var l = arguments.length; - return l - ? l > 1 - ? fn.apply(ctx, arguments) - : fn.call(ctx, a) - : fn.call(ctx) - } - // record original fn length - boundFn._length = fn.length; - return boundFn -} - -/** - * Convert an Array-like object to a real Array. - */ -function toArray (list, start) { - start = start || 0; - var i = list.length - start; - var ret = new Array(i); - while (i--) { - ret[i] = list[i + start]; - } - return ret -} - -/** - * Mix properties into target object. - */ -function extend (to, _from) { - for (var key in _from) { - to[key] = _from[key]; - } - return to -} - -/** - * Merge an Array of Objects into a single Object. - */ -function toObject (arr) { - var res = {}; - for (var i = 0; i < arr.length; i++) { - if (arr[i]) { - extend(res, arr[i]); - } - } - return res -} - -/** - * Perform no operation. - * Stubbing args to make Flow happy without leaving useless transpiled code - * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/) - */ -function noop (a, b, c) {} - -/** - * Always return false. - */ -var no = function (a, b, c) { return false; }; - -/** - * Return same value - */ -var identity = function (_) { return _; }; - -/** - * Generate a static keys string from compiler modules. - */ - - -/** - * Check if two values are loosely equal - that is, - * if they are plain objects, do they have the same shape? - */ -function looseEqual (a, b) { - if (a === b) { return true } - var isObjectA = isObject(a); - var isObjectB = isObject(b); - if (isObjectA && isObjectB) { - try { - var isArrayA = Array.isArray(a); - var isArrayB = Array.isArray(b); - if (isArrayA && isArrayB) { - return a.length === b.length && a.every(function (e, i) { - return looseEqual(e, b[i]) - }) - } else if (!isArrayA && !isArrayB) { - var keysA = Object.keys(a); - var keysB = Object.keys(b); - return keysA.length === keysB.length && keysA.every(function (key) { - return looseEqual(a[key], b[key]) - }) - } else { - /* istanbul ignore next */ - return false - } - } catch (e) { - /* istanbul ignore next */ - return false - } - } else if (!isObjectA && !isObjectB) { - return String(a) === String(b) - } else { - return false - } -} - -function looseIndexOf (arr, val) { - for (var i = 0; i < arr.length; i++) { - if (looseEqual(arr[i], val)) { return i } - } - return -1 -} - -/** - * Ensure a function is called only once. - */ -function once (fn) { - var called = false; - return function () { - if (!called) { - called = true; - fn.apply(this, arguments); - } - } -} - -var SSR_ATTR = 'data-server-rendered'; - -var ASSET_TYPES = [ - 'component', - 'directive', - 'filter' -]; - -var LIFECYCLE_HOOKS = [ - 'beforeCreate', - 'created', - 'beforeMount', - 'mounted', - 'beforeUpdate', - 'updated', - 'beforeDestroy', - 'destroyed', - 'activated', - 'deactivated', - 'errorCaptured' -]; - -/* */ - -var config = ({ - /** - * Option merge strategies (used in core/util/options) - */ - optionMergeStrategies: Object.create(null), - - /** - * Whether to suppress warnings. - */ - silent: false, - - /** - * Show production mode tip message on boot? - */ - productionTip: process.env.NODE_ENV !== 'production', - - /** - * Whether to enable devtools - */ - devtools: process.env.NODE_ENV !== 'production', - - /** - * Whether to record perf - */ - performance: false, - - /** - * Error handler for watcher errors - */ - errorHandler: null, - - /** - * Warn handler for watcher warns - */ - warnHandler: null, - - /** - * Ignore certain custom elements - */ - ignoredElements: [], - - /** - * Custom user key aliases for v-on - */ - keyCodes: Object.create(null), - - /** - * Check if a tag is reserved so that it cannot be registered as a - * component. This is platform-dependent and may be overwritten. - */ - isReservedTag: no, - - /** - * Check if an attribute is reserved so that it cannot be used as a component - * prop. This is platform-dependent and may be overwritten. - */ - isReservedAttr: no, - - /** - * Check if a tag is an unknown element. - * Platform-dependent. - */ - isUnknownElement: no, - - /** - * Get the namespace of an element - */ - getTagNamespace: noop, - - /** - * Parse the real tag name for the specific platform. - */ - parsePlatformTagName: identity, - - /** - * Check if an attribute must be bound using property, e.g. value - * Platform-dependent. - */ - mustUseProp: no, - - /** - * Exposed for legacy reasons - */ - _lifecycleHooks: LIFECYCLE_HOOKS -}); - -/* */ - -var emptyObject = Object.freeze({}); - -/** - * Check if a string starts with $ or _ - */ -function isReserved (str) { - var c = (str + '').charCodeAt(0); - return c === 0x24 || c === 0x5F -} - -/** - * Define a property. - */ -function def (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value: val, - enumerable: !!enumerable, - writable: true, - configurable: true - }); -} - -/** - * Parse simple path. - */ -var bailRE = /[^\w.$]/; -function parsePath (path) { - if (bailRE.test(path)) { - return - } - var segments = path.split('.'); - return function (obj) { - for (var i = 0; i < segments.length; i++) { - if (!obj) { return } - obj = obj[segments[i]]; - } - return obj - } -} - -/* */ - -// can we use __proto__? -var hasProto = '__proto__' in {}; - -// Browser environment sniffing -var inBrowser = typeof window !== 'undefined'; -var UA = inBrowser && window.navigator.userAgent.toLowerCase(); -var isIE = UA && /msie|trident/.test(UA); -var isIE9 = UA && UA.indexOf('msie 9.0') > 0; -var isEdge = UA && UA.indexOf('edge/') > 0; -var isAndroid = UA && UA.indexOf('android') > 0; -var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); -var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; - -// Firefox has a "watch" function on Object.prototype... -var nativeWatch = ({}).watch; - -var supportsPassive = false; -if (inBrowser) { - try { - var opts = {}; - Object.defineProperty(opts, 'passive', ({ - get: function get () { - /* istanbul ignore next */ - supportsPassive = true; - } - })); // https://github.com/facebook/flow/issues/285 - window.addEventListener('test-passive', null, opts); - } catch (e) {} -} - -// this needs to be lazy-evaled because vue may be required before -// vue-server-renderer can set VUE_ENV -var _isServer; -var isServerRendering = function () { - if (_isServer === undefined) { - /* istanbul ignore if */ - if (!inBrowser && typeof global !== 'undefined') { - // detect presence of vue-server-renderer and avoid - // Webpack shimming the process - _isServer = global['process'].env.VUE_ENV === 'server'; - } else { - _isServer = false; - } - } - return _isServer -}; - -// detect devtools -var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; - -/* istanbul ignore next */ -function isNative (Ctor) { - return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) -} - -var hasSymbol = - typeof Symbol !== 'undefined' && isNative(Symbol) && - typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); - -var _Set; -/* istanbul ignore if */ // $flow-disable-line -if (typeof Set !== 'undefined' && isNative(Set)) { - // use native Set when available. - _Set = Set; -} else { - // a non-standard Set polyfill that only works with primitive keys. - _Set = (function () { - function Set () { - this.set = Object.create(null); - } - Set.prototype.has = function has (key) { - return this.set[key] === true - }; - Set.prototype.add = function add (key) { - this.set[key] = true; - }; - Set.prototype.clear = function clear () { - this.set = Object.create(null); - }; - - return Set; - }()); -} - -/* */ - -var warn = noop; -var tip = noop; -var generateComponentTrace = (noop); // work around flow check -var formatComponentName = (noop); - -if (process.env.NODE_ENV !== 'production') { - var hasConsole = typeof console !== 'undefined'; - var classifyRE = /(?:^|[-_])(\w)/g; - var classify = function (str) { return str - .replace(classifyRE, function (c) { return c.toUpperCase(); }) - .replace(/[-_]/g, ''); }; - - warn = function (msg, vm) { - var trace = vm ? generateComponentTrace(vm) : ''; - - if (config.warnHandler) { - config.warnHandler.call(null, msg, vm, trace); - } else if (hasConsole && (!config.silent)) { - console.error(("[Vue warn]: " + msg + trace)); - } - }; - - tip = function (msg, vm) { - if (hasConsole && (!config.silent)) { - console.warn("[Vue tip]: " + msg + ( - vm ? generateComponentTrace(vm) : '' - )); - } - }; - - formatComponentName = function (vm, includeFile) { - if (vm.$root === vm) { - return '' - } - var options = typeof vm === 'function' && vm.cid != null - ? vm.options - : vm._isVue - ? vm.$options || vm.constructor.options - : vm || {}; - var name = options.name || options._componentTag; - var file = options.__file; - if (!name && file) { - var match = file.match(/([^/\\]+)\.vue$/); - name = match && match[1]; - } - - return ( - (name ? ("<" + (classify(name)) + ">") : "") + - (file && includeFile !== false ? (" at " + file) : '') - ) - }; - - var repeat = function (str, n) { - var res = ''; - while (n) { - if (n % 2 === 1) { res += str; } - if (n > 1) { str += str; } - n >>= 1; - } - return res - }; - - generateComponentTrace = function (vm) { - if (vm._isVue && vm.$parent) { - var tree = []; - var currentRecursiveSequence = 0; - while (vm) { - if (tree.length > 0) { - var last = tree[tree.length - 1]; - if (last.constructor === vm.constructor) { - currentRecursiveSequence++; - vm = vm.$parent; - continue - } else if (currentRecursiveSequence > 0) { - tree[tree.length - 1] = [last, currentRecursiveSequence]; - currentRecursiveSequence = 0; - } - } - tree.push(vm); - vm = vm.$parent; - } - return '\n\nfound in\n\n' + tree - .map(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) - ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") - : formatComponentName(vm))); }) - .join('\n') - } else { - return ("\n\n(found in " + (formatComponentName(vm)) + ")") - } - }; -} - -/* */ - - -var uid$1 = 0; - -/** - * A dep is an observable that can have multiple - * directives subscribing to it. - */ -var Dep = function Dep () { - this.id = uid$1++; - this.subs = []; -}; - -Dep.prototype.addSub = function addSub (sub) { - this.subs.push(sub); -}; - -Dep.prototype.removeSub = function removeSub (sub) { - remove(this.subs, sub); -}; - -Dep.prototype.depend = function depend () { - if (Dep.target) { - Dep.target.addDep(this); - } -}; - -Dep.prototype.notify = function notify () { - // stabilize the subscriber list first - var subs = this.subs.slice(); - for (var i = 0, l = subs.length; i < l; i++) { - subs[i].update(); - } -}; - -// the current target watcher being evaluated. -// this is globally unique because there could be only one -// watcher being evaluated at any time. -Dep.target = null; -var targetStack = []; - -function pushTarget (_target) { - if (Dep.target) { targetStack.push(Dep.target); } - Dep.target = _target; -} - -function popTarget () { - Dep.target = targetStack.pop(); -} - -/* */ - -var VNode = function VNode ( - tag, - data, - children, - text, - elm, - context, - componentOptions, - asyncFactory -) { - this.tag = tag; - this.data = data; - this.children = children; - this.text = text; - this.elm = elm; - this.ns = undefined; - this.context = context; - this.functionalContext = undefined; - this.functionalOptions = undefined; - this.functionalScopeId = undefined; - this.key = data && data.key; - this.componentOptions = componentOptions; - this.componentInstance = undefined; - this.parent = undefined; - this.raw = false; - this.isStatic = false; - this.isRootInsert = true; - this.isComment = false; - this.isCloned = false; - this.isOnce = false; - this.asyncFactory = asyncFactory; - this.asyncMeta = undefined; - this.isAsyncPlaceholder = false; -}; - -var prototypeAccessors = { child: { configurable: true } }; - -// DEPRECATED: alias for componentInstance for backwards compat. -/* istanbul ignore next */ -prototypeAccessors.child.get = function () { - return this.componentInstance -}; - -Object.defineProperties( VNode.prototype, prototypeAccessors ); - -var createEmptyVNode = function (text) { - if ( text === void 0 ) text = ''; - - var node = new VNode(); - node.text = text; - node.isComment = true; - return node -}; - -function createTextVNode (val) { - return new VNode(undefined, undefined, undefined, String(val)) -} - -// optimized shallow clone -// used for static nodes and slot nodes because they may be reused across -// multiple renders, cloning them avoids errors when DOM manipulations rely -// on their elm reference. -function cloneVNode (vnode, deep) { - var cloned = new VNode( - vnode.tag, - vnode.data, - vnode.children, - vnode.text, - vnode.elm, - vnode.context, - vnode.componentOptions, - vnode.asyncFactory - ); - cloned.ns = vnode.ns; - cloned.isStatic = vnode.isStatic; - cloned.key = vnode.key; - cloned.isComment = vnode.isComment; - cloned.isCloned = true; - if (deep && vnode.children) { - cloned.children = cloneVNodes(vnode.children); - } - return cloned -} - -function cloneVNodes (vnodes, deep) { - var len = vnodes.length; - var res = new Array(len); - for (var i = 0; i < len; i++) { - res[i] = cloneVNode(vnodes[i], deep); - } - return res -} - -/* - * not type checking this file because flow doesn't play well with - * dynamically accessing methods on Array prototype - */ - -var arrayProto = Array.prototype; -var arrayMethods = Object.create(arrayProto);[ - 'push', - 'pop', - 'shift', - 'unshift', - 'splice', - 'sort', - 'reverse' -] -.forEach(function (method) { - // cache original method - var original = arrayProto[method]; - def(arrayMethods, method, function mutator () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - var result = original.apply(this, args); - var ob = this.__ob__; - var inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - break - case 'splice': - inserted = args.slice(2); - break - } - if (inserted) { ob.observeArray(inserted); } - // notify change - ob.dep.notify(); - return result - }); -}); - -/* */ - -var arrayKeys = Object.getOwnPropertyNames(arrayMethods); - -/** - * By default, when a reactive property is set, the new value is - * also converted to become reactive. However when passing down props, - * we don't want to force conversion because the value may be a nested value - * under a frozen data structure. Converting it would defeat the optimization. - */ -var observerState = { - shouldConvert: true -}; - -/** - * Observer class that are attached to each observed - * object. Once attached, the observer converts target - * object's property keys into getter/setters that - * collect dependencies and dispatches updates. - */ -var Observer = function Observer (value) { - this.value = value; - this.dep = new Dep(); - this.vmCount = 0; - def(value, '__ob__', this); - if (Array.isArray(value)) { - var augment = hasProto - ? protoAugment - : copyAugment; - augment(value, arrayMethods, arrayKeys); - this.observeArray(value); - } else { - this.walk(value); - } -}; - -/** - * Walk through each property and convert them into - * getter/setters. This method should only be called when - * value type is Object. - */ -Observer.prototype.walk = function walk (obj) { - var keys = Object.keys(obj); - for (var i = 0; i < keys.length; i++) { - defineReactive(obj, keys[i], obj[keys[i]]); - } -}; - -/** - * Observe a list of Array items. - */ -Observer.prototype.observeArray = function observeArray (items) { - for (var i = 0, l = items.length; i < l; i++) { - observe(items[i]); - } -}; - -// helpers - -/** - * Augment an target Object or Array by intercepting - * the prototype chain using __proto__ - */ -function protoAugment (target, src, keys) { - /* eslint-disable no-proto */ - target.__proto__ = src; - /* eslint-enable no-proto */ -} - -/** - * Augment an target Object or Array by defining - * hidden properties. - */ -/* istanbul ignore next */ -function copyAugment (target, src, keys) { - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - def(target, key, src[key]); - } -} - -/** - * Attempt to create an observer instance for a value, - * returns the new observer if successfully observed, - * or the existing observer if the value already has one. - */ -function observe (value, asRootData) { - if (!isObject(value) || value instanceof VNode) { - return - } - var ob; - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__; - } else if ( - observerState.shouldConvert && - !isServerRendering() && - (Array.isArray(value) || isPlainObject(value)) && - Object.isExtensible(value) && - !value._isVue - ) { - ob = new Observer(value); - } - if (asRootData && ob) { - ob.vmCount++; - } - return ob -} - -/** - * Define a reactive property on an Object. - */ -function defineReactive ( - obj, - key, - val, - customSetter, - shallow -) { - var dep = new Dep(); - - var property = Object.getOwnPropertyDescriptor(obj, key); - if (property && property.configurable === false) { - return - } - - // cater for pre-defined getter/setters - var getter = property && property.get; - var setter = property && property.set; - - var childOb = !shallow && observe(val); - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get: function reactiveGetter () { - var value = getter ? getter.call(obj) : val; - if (Dep.target) { - dep.depend(); - if (childOb) { - childOb.dep.depend(); - if (Array.isArray(value)) { - dependArray(value); - } - } - } - return value - }, - set: function reactiveSetter (newVal) { - var value = getter ? getter.call(obj) : val; - /* eslint-disable no-self-compare */ - if (newVal === value || (newVal !== newVal && value !== value)) { - return - } - /* eslint-enable no-self-compare */ - if (process.env.NODE_ENV !== 'production' && customSetter) { - customSetter(); - } - if (setter) { - setter.call(obj, newVal); - } else { - val = newVal; - } - childOb = !shallow && observe(newVal); - dep.notify(); - } - }); -} - -/** - * Set a property on an object. Adds the new property and - * triggers change notification if the property doesn't - * already exist. - */ -function set (target, key, val) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.length = Math.max(target.length, key); - target.splice(key, 1, val); - return val - } - if (hasOwn(target, key)) { - target[key] = val; - return val - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid adding reactive properties to a Vue instance or its root $data ' + - 'at runtime - declare it upfront in the data option.' - ); - return val - } - if (!ob) { - target[key] = val; - return val - } - defineReactive(ob.value, key, val); - ob.dep.notify(); - return val -} - -/** - * Delete a property and trigger change if necessary. - */ -function del (target, key) { - if (Array.isArray(target) && isValidArrayIndex(key)) { - target.splice(key, 1); - return - } - var ob = (target).__ob__; - if (target._isVue || (ob && ob.vmCount)) { - process.env.NODE_ENV !== 'production' && warn( - 'Avoid deleting properties on a Vue instance or its root $data ' + - '- just set it to null.' - ); - return - } - if (!hasOwn(target, key)) { - return - } - delete target[key]; - if (!ob) { - return - } - ob.dep.notify(); -} - -/** - * Collect dependencies on array elements when the array is touched, since - * we cannot intercept array element access like property getters. - */ -function dependArray (value) { - for (var e = (void 0), i = 0, l = value.length; i < l; i++) { - e = value[i]; - e && e.__ob__ && e.__ob__.dep.depend(); - if (Array.isArray(e)) { - dependArray(e); - } - } -} - -/* */ - -/** - * Option overwriting strategies are functions that handle - * how to merge a parent option value and a child option - * value into the final value. - */ -var strats = config.optionMergeStrategies; - -/** - * Options with restrictions - */ -if (process.env.NODE_ENV !== 'production') { - strats.el = strats.propsData = function (parent, child, vm, key) { - if (!vm) { - warn( - "option \"" + key + "\" can only be used during instance " + - 'creation with the `new` keyword.' - ); - } - return defaultStrat(parent, child) - }; -} - -/** - * Helper that recursively merges two data objects together. - */ -function mergeData (to, from) { - if (!from) { return to } - var key, toVal, fromVal; - var keys = Object.keys(from); - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - toVal = to[key]; - fromVal = from[key]; - if (!hasOwn(to, key)) { - set(to, key, fromVal); - } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { - mergeData(toVal, fromVal); - } - } - return to -} - -/** - * Data - */ -function mergeDataOrFn ( - parentVal, - childVal, - vm -) { - if (!vm) { - // in a Vue.extend merge, both should be functions - if (!childVal) { - return parentVal - } - if (!parentVal) { - return childVal - } - // when parentVal & childVal are both present, - // we need to return a function that returns the - // merged result of both functions... no need to - // check if parentVal is a function here because - // it has to be a function to pass previous merges. - return function mergedDataFn () { - return mergeData( - typeof childVal === 'function' ? childVal.call(this) : childVal, - typeof parentVal === 'function' ? parentVal.call(this) : parentVal - ) - } - } else if (parentVal || childVal) { - return function mergedInstanceDataFn () { - // instance merge - var instanceData = typeof childVal === 'function' - ? childVal.call(vm) - : childVal; - var defaultData = typeof parentVal === 'function' - ? parentVal.call(vm) - : parentVal; - if (instanceData) { - return mergeData(instanceData, defaultData) - } else { - return defaultData - } - } - } -} - -strats.data = function ( - parentVal, - childVal, - vm -) { - if (!vm) { - if (childVal && typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ); - - return parentVal - } - return mergeDataOrFn.call(this, parentVal, childVal) - } - - return mergeDataOrFn(parentVal, childVal, vm) -}; - -/** - * Hooks and props are merged as arrays. - */ -function mergeHook ( - parentVal, - childVal -) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : Array.isArray(childVal) - ? childVal - : [childVal] - : parentVal -} - -LIFECYCLE_HOOKS.forEach(function (hook) { - strats[hook] = mergeHook; -}); - -/** - * Assets - * - * When a vm is present (instance creation), we need to do - * a three-way merge between constructor options, instance - * options and parent options. - */ -function mergeAssets ( - parentVal, - childVal, - vm, - key -) { - var res = Object.create(parentVal || null); - if (childVal) { - process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm); - return extend(res, childVal) - } else { - return res - } -} - -ASSET_TYPES.forEach(function (type) { - strats[type + 's'] = mergeAssets; -}); - -/** - * Watchers. - * - * Watchers hashes should not overwrite one - * another, so we merge them as arrays. - */ -strats.watch = function ( - parentVal, - childVal, - vm, - key -) { - // work around Firefox's Object.prototype.watch... - if (parentVal === nativeWatch) { parentVal = undefined; } - if (childVal === nativeWatch) { childVal = undefined; } - /* istanbul ignore if */ - if (!childVal) { return Object.create(parentVal || null) } - if (process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = {}; - extend(ret, parentVal); - for (var key$1 in childVal) { - var parent = ret[key$1]; - var child = childVal[key$1]; - if (parent && !Array.isArray(parent)) { - parent = [parent]; - } - ret[key$1] = parent - ? parent.concat(child) - : Array.isArray(child) ? child : [child]; - } - return ret -}; - -/** - * Other object hashes. - */ -strats.props = -strats.methods = -strats.inject = -strats.computed = function ( - parentVal, - childVal, - vm, - key -) { - if (childVal && process.env.NODE_ENV !== 'production') { - assertObjectType(key, childVal, vm); - } - if (!parentVal) { return childVal } - var ret = Object.create(null); - extend(ret, parentVal); - if (childVal) { extend(ret, childVal); } - return ret -}; -strats.provide = mergeDataOrFn; - -/** - * Default strategy. - */ -var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal -}; - -/** - * Validate component names - */ -function checkComponents (options) { - for (var key in options.components) { - var lower = key.toLowerCase(); - if (isBuiltInTag(lower) || config.isReservedTag(lower)) { - warn( - 'Do not use built-in or reserved HTML elements as component ' + - 'id: ' + key - ); - } - } -} - -/** - * Ensure all props option syntax are normalized into the - * Object-based format. - */ -function normalizeProps (options, vm) { - var props = options.props; - if (!props) { return } - var res = {}; - var i, val, name; - if (Array.isArray(props)) { - i = props.length; - while (i--) { - val = props[i]; - if (typeof val === 'string') { - name = camelize(val); - res[name] = { type: null }; - } else if (process.env.NODE_ENV !== 'production') { - warn('props must be strings when using array syntax.'); - } - } - } else if (isPlainObject(props)) { - for (var key in props) { - val = props[key]; - name = camelize(key); - res[name] = isPlainObject(val) - ? val - : { type: val }; - } - } else if (process.env.NODE_ENV !== 'production') { - warn( - "Invalid value for option \"props\": expected an Array or an Object, " + - "but got " + (toRawType(props)) + ".", - vm - ); - } - options.props = res; -} - -/** - * Normalize all injections into Object-based format - */ -function normalizeInject (options, vm) { - var inject = options.inject; - var normalized = options.inject = {}; - if (Array.isArray(inject)) { - for (var i = 0; i < inject.length; i++) { - normalized[inject[i]] = { from: inject[i] }; - } - } else if (isPlainObject(inject)) { - for (var key in inject) { - var val = inject[key]; - normalized[key] = isPlainObject(val) - ? extend({ from: key }, val) - : { from: val }; - } - } else if (process.env.NODE_ENV !== 'production' && inject) { - warn( - "Invalid value for option \"inject\": expected an Array or an Object, " + - "but got " + (toRawType(inject)) + ".", - vm - ); - } -} - -/** - * Normalize raw function directives into object format. - */ -function normalizeDirectives (options) { - var dirs = options.directives; - if (dirs) { - for (var key in dirs) { - var def = dirs[key]; - if (typeof def === 'function') { - dirs[key] = { bind: def, update: def }; - } - } - } -} - -function assertObjectType (name, value, vm) { - if (!isPlainObject(value)) { - warn( - "Invalid value for option \"" + name + "\": expected an Object, " + - "but got " + (toRawType(value)) + ".", - vm - ); - } -} - -/** - * Merge two option objects into a new one. - * Core utility used in both instantiation and inheritance. - */ -function mergeOptions ( - parent, - child, - vm -) { - if (process.env.NODE_ENV !== 'production') { - checkComponents(child); - } - - if (typeof child === 'function') { - child = child.options; - } - - normalizeProps(child, vm); - normalizeInject(child, vm); - normalizeDirectives(child); - var extendsFrom = child.extends; - if (extendsFrom) { - parent = mergeOptions(parent, extendsFrom, vm); - } - if (child.mixins) { - for (var i = 0, l = child.mixins.length; i < l; i++) { - parent = mergeOptions(parent, child.mixins[i], vm); - } - } - var options = {}; - var key; - for (key in parent) { - mergeField(key); - } - for (key in child) { - if (!hasOwn(parent, key)) { - mergeField(key); - } - } - function mergeField (key) { - var strat = strats[key] || defaultStrat; - options[key] = strat(parent[key], child[key], vm, key); - } - return options -} - -/** - * Resolve an asset. - * This function is used because child instances need access - * to assets defined in its ancestor chain. - */ -function resolveAsset ( - options, - type, - id, - warnMissing -) { - /* istanbul ignore if */ - if (typeof id !== 'string') { - return - } - var assets = options[type]; - // check local registration variations first - if (hasOwn(assets, id)) { return assets[id] } - var camelizedId = camelize(id); - if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } - var PascalCaseId = capitalize(camelizedId); - if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } - // fallback to prototype chain - var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; - if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { - warn( - 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, - options - ); - } - return res -} - -/* */ - -function validateProp ( - key, - propOptions, - propsData, - vm -) { - var prop = propOptions[key]; - var absent = !hasOwn(propsData, key); - var value = propsData[key]; - // handle boolean props - if (isType(Boolean, prop.type)) { - if (absent && !hasOwn(prop, 'default')) { - value = false; - } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) { - value = true; - } - } - // check default value - if (value === undefined) { - value = getPropDefaultValue(vm, prop, key); - // since the default value is a fresh copy, - // make sure to observe it. - var prevShouldConvert = observerState.shouldConvert; - observerState.shouldConvert = true; - observe(value); - observerState.shouldConvert = prevShouldConvert; - } - if (process.env.NODE_ENV !== 'production') { - assertProp(prop, key, value, vm, absent); - } - return value -} - -/** - * Get the default value of a prop. - */ -function getPropDefaultValue (vm, prop, key) { - // no default, return undefined - if (!hasOwn(prop, 'default')) { - return undefined - } - var def = prop.default; - // warn against non-factory defaults for Object & Array - if (process.env.NODE_ENV !== 'production' && isObject(def)) { - warn( - 'Invalid default value for prop "' + key + '": ' + - 'Props with type Object/Array must use a factory function ' + - 'to return the default value.', - vm - ); - } - // the raw prop value was also undefined from previous render, - // return previous default value to avoid unnecessary watcher trigger - if (vm && vm.$options.propsData && - vm.$options.propsData[key] === undefined && - vm._props[key] !== undefined - ) { - return vm._props[key] - } - // call factory function for non-Function types - // a value is Function if its prototype is function even across different execution context - return typeof def === 'function' && getType(prop.type) !== 'Function' - ? def.call(vm) - : def -} - -/** - * Assert whether a prop is valid. - */ -function assertProp ( - prop, - name, - value, - vm, - absent -) { - if (prop.required && absent) { - warn( - 'Missing required prop: "' + name + '"', - vm - ); - return - } - if (value == null && !prop.required) { - return - } - var type = prop.type; - var valid = !type || type === true; - var expectedTypes = []; - if (type) { - if (!Array.isArray(type)) { - type = [type]; - } - for (var i = 0; i < type.length && !valid; i++) { - var assertedType = assertType(value, type[i]); - expectedTypes.push(assertedType.expectedType || ''); - valid = assertedType.valid; - } - } - if (!valid) { - warn( - "Invalid prop: type check failed for prop \"" + name + "\"." + - " Expected " + (expectedTypes.map(capitalize).join(', ')) + - ", got " + (toRawType(value)) + ".", - vm - ); - return - } - var validator = prop.validator; - if (validator) { - if (!validator(value)) { - warn( - 'Invalid prop: custom validator check failed for prop "' + name + '".', - vm - ); - } - } -} - -var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; - -function assertType (value, type) { - var valid; - var expectedType = getType(type); - if (simpleCheckRE.test(expectedType)) { - var t = typeof value; - valid = t === expectedType.toLowerCase(); - // for primitive wrapper objects - if (!valid && t === 'object') { - valid = value instanceof type; - } - } else if (expectedType === 'Object') { - valid = isPlainObject(value); - } else if (expectedType === 'Array') { - valid = Array.isArray(value); - } else { - valid = value instanceof type; - } - return { - valid: valid, - expectedType: expectedType - } -} - -/** - * Use function string name to check built-in types, - * because a simple equality check will fail when running - * across different vms / iframes. - */ -function getType (fn) { - var match = fn && fn.toString().match(/^\s*function (\w+)/); - return match ? match[1] : '' -} - -function isType (type, fn) { - if (!Array.isArray(fn)) { - return getType(fn) === getType(type) - } - for (var i = 0, len = fn.length; i < len; i++) { - if (getType(fn[i]) === getType(type)) { - return true - } - } - /* istanbul ignore next */ - return false -} - -/* */ - -function handleError (err, vm, info) { - if (vm) { - var cur = vm; - while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured; - if (hooks) { - for (var i = 0; i < hooks.length; i++) { - try { - var capture = hooks[i].call(cur, err, vm, info) === false; - if (capture) { return } - } catch (e) { - globalHandleError(e, cur, 'errorCaptured hook'); - } - } - } - } - } - globalHandleError(err, vm, info); -} - -function globalHandleError (err, vm, info) { - if (config.errorHandler) { - try { - return config.errorHandler.call(null, err, vm, info) - } catch (e) { - logError(e, null, 'config.errorHandler'); - } - } - logError(err, vm, info); -} - -function logError (err, vm, info) { - if (process.env.NODE_ENV !== 'production') { - warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); - } - /* istanbul ignore else */ - if (inBrowser && typeof console !== 'undefined') { - console.error(err); - } else { - throw err - } -} - -/* */ -/* globals MessageChannel */ - -var callbacks = []; -var pending = false; - -function flushCallbacks () { - pending = false; - var copies = callbacks.slice(0); - callbacks.length = 0; - for (var i = 0; i < copies.length; i++) { - copies[i](); - } -} - -// Here we have async deferring wrappers using both micro and macro tasks. -// In < 2.4 we used micro tasks everywhere, but there are some scenarios where -// micro tasks have too high a priority and fires in between supposedly -// sequential events (e.g. #4521, #6690) or even between bubbling of the same -// event (#6566). However, using macro tasks everywhere also has subtle problems -// when state is changed right before repaint (e.g. #6813, out-in transitions). -// Here we use micro task by default, but expose a way to force macro task when -// needed (e.g. in event handlers attached by v-on). -var microTimerFunc; -var macroTimerFunc; -var useMacroTask = false; - -// Determine (macro) Task defer implementation. -// Technically setImmediate should be the ideal choice, but it's only available -// in IE. The only polyfill that consistently queues the callback after all DOM -// events triggered in the same loop is by using MessageChannel. -/* istanbul ignore if */ -if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { - macroTimerFunc = function () { - setImmediate(flushCallbacks); - }; -} else if (typeof MessageChannel !== 'undefined' && ( - isNative(MessageChannel) || - // PhantomJS - MessageChannel.toString() === '[object MessageChannelConstructor]' -)) { - var channel = new MessageChannel(); - var port = channel.port2; - channel.port1.onmessage = flushCallbacks; - macroTimerFunc = function () { - port.postMessage(1); - }; -} else { - /* istanbul ignore next */ - macroTimerFunc = function () { - setTimeout(flushCallbacks, 0); - }; -} - -// Determine MicroTask defer implementation. -/* istanbul ignore next, $flow-disable-line */ -if (typeof Promise !== 'undefined' && isNative(Promise)) { - var p = Promise.resolve(); - microTimerFunc = function () { - p.then(flushCallbacks); - // in problematic UIWebViews, Promise.then doesn't completely break, but - // it can get stuck in a weird state where callbacks are pushed into the - // microtask queue but the queue isn't being flushed, until the browser - // needs to do some other work, e.g. handle a timer. Therefore we can - // "force" the microtask queue to be flushed by adding an empty timer. - if (isIOS) { setTimeout(noop); } - }; -} else { - // fallback to macro - microTimerFunc = macroTimerFunc; -} - -/** - * Wrap a function so that if any code inside triggers state change, - * the changes are queued using a Task instead of a MicroTask. - */ -function withMacroTask (fn) { - return fn._withTask || (fn._withTask = function () { - useMacroTask = true; - var res = fn.apply(null, arguments); - useMacroTask = false; - return res - }) -} - -function nextTick (cb, ctx) { - var _resolve; - callbacks.push(function () { - if (cb) { - try { - cb.call(ctx); - } catch (e) { - handleError(e, ctx, 'nextTick'); - } - } else if (_resolve) { - _resolve(ctx); - } - }); - if (!pending) { - pending = true; - if (useMacroTask) { - macroTimerFunc(); - } else { - microTimerFunc(); - } - } - // $flow-disable-line - if (!cb && typeof Promise !== 'undefined') { - return new Promise(function (resolve) { - _resolve = resolve; - }) - } -} - -/* */ - -/* not type checking this file because flow doesn't play well with Proxy */ - -var initProxy; - -if (process.env.NODE_ENV !== 'production') { - var allowedGlobals = makeMap( - 'Infinity,undefined,NaN,isFinite,isNaN,' + - 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + - 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + - 'require' // for Webpack/Browserify - ); - - var warnNonPresent = function (target, key) { - warn( - "Property or method \"" + key + "\" is not defined on the instance but " + - 'referenced during render. Make sure that this property is reactive, ' + - 'either in the data option, or for class-based components, by ' + - 'initializing the property. ' + - 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', - target - ); - }; - - var hasProxy = - typeof Proxy !== 'undefined' && - Proxy.toString().match(/native code/); - - if (hasProxy) { - var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); - config.keyCodes = new Proxy(config.keyCodes, { - set: function set (target, key, value) { - if (isBuiltInModifier(key)) { - warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); - return false - } else { - target[key] = value; - return true - } - } - }); - } - - var hasHandler = { - has: function has (target, key) { - var has = key in target; - var isAllowed = allowedGlobals(key) || key.charAt(0) === '_'; - if (!has && !isAllowed) { - warnNonPresent(target, key); - } - return has || !isAllowed - } - }; - - var getHandler = { - get: function get (target, key) { - if (typeof key === 'string' && !(key in target)) { - warnNonPresent(target, key); - } - return target[key] - } - }; - - initProxy = function initProxy (vm) { - if (hasProxy) { - // determine which proxy handler to use - var options = vm.$options; - var handlers = options.render && options.render._withStripped - ? getHandler - : hasHandler; - vm._renderProxy = new Proxy(vm, handlers); - } else { - vm._renderProxy = vm; - } - }; -} - -var mark; -var measure; - -if (process.env.NODE_ENV !== 'production') { - var perf = inBrowser && window.performance; - /* istanbul ignore if */ - if ( - perf && - perf.mark && - perf.measure && - perf.clearMarks && - perf.clearMeasures - ) { - mark = function (tag) { return perf.mark(tag); }; - measure = function (name, startTag, endTag) { - perf.measure(name, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - perf.clearMeasures(name); - }; - } -} - -/* */ - -var normalizeEvent = cached(function (name) { - var passive = name.charAt(0) === '&'; - name = passive ? name.slice(1) : name; - var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first - name = once$$1 ? name.slice(1) : name; - var capture = name.charAt(0) === '!'; - name = capture ? name.slice(1) : name; - return { - name: name, - once: once$$1, - capture: capture, - passive: passive - } -}); - -function createFnInvoker (fns) { - function invoker () { - var arguments$1 = arguments; - - var fns = invoker.fns; - if (Array.isArray(fns)) { - var cloned = fns.slice(); - for (var i = 0; i < cloned.length; i++) { - cloned[i].apply(null, arguments$1); - } - } else { - // return handler return value for single handlers - return fns.apply(null, arguments) - } - } - invoker.fns = fns; - return invoker -} - -function updateListeners ( - on, - oldOn, - add, - remove$$1, - vm -) { - var name, cur, old, event; - for (name in on) { - cur = on[name]; - old = oldOn[name]; - event = normalizeEvent(name); - if (isUndef(cur)) { - process.env.NODE_ENV !== 'production' && warn( - "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), - vm - ); - } else if (isUndef(old)) { - if (isUndef(cur.fns)) { - cur = on[name] = createFnInvoker(cur); - } - add(event.name, cur, event.once, event.capture, event.passive); - } else if (cur !== old) { - old.fns = cur; - on[name] = old; - } - } - for (name in oldOn) { - if (isUndef(on[name])) { - event = normalizeEvent(name); - remove$$1(event.name, oldOn[name], event.capture); - } - } -} - -/* */ - -function mergeVNodeHook (def, hookKey, hook) { - var invoker; - var oldHook = def[hookKey]; - - function wrappedHook () { - hook.apply(this, arguments); - // important: remove merged hook to ensure it's called only once - // and prevent memory leak - remove(invoker.fns, wrappedHook); - } - - if (isUndef(oldHook)) { - // no existing hook - invoker = createFnInvoker([wrappedHook]); - } else { - /* istanbul ignore if */ - if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { - // already a merged invoker - invoker = oldHook; - invoker.fns.push(wrappedHook); - } else { - // existing plain hook - invoker = createFnInvoker([oldHook, wrappedHook]); - } - } - - invoker.merged = true; - def[hookKey] = invoker; -} - -/* */ - -function extractPropsFromVNodeData ( - data, - Ctor, - tag -) { - // we are only extracting raw values here. - // validation and default values are handled in the child - // component itself. - var propOptions = Ctor.options.props; - if (isUndef(propOptions)) { - return - } - var res = {}; - var attrs = data.attrs; - var props = data.props; - if (isDef(attrs) || isDef(props)) { - for (var key in propOptions) { - var altKey = hyphenate(key); - if (process.env.NODE_ENV !== 'production') { - var keyInLowerCase = key.toLowerCase(); - if ( - key !== keyInLowerCase && - attrs && hasOwn(attrs, keyInLowerCase) - ) { - tip( - "Prop \"" + keyInLowerCase + "\" is passed to component " + - (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + - " \"" + key + "\". " + - "Note that HTML attributes are case-insensitive and camelCased " + - "props need to use their kebab-case equivalents when using in-DOM " + - "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." - ); - } - } - checkProp(res, props, key, altKey, true) || - checkProp(res, attrs, key, altKey, false); - } - } - return res -} - -function checkProp ( - res, - hash, - key, - altKey, - preserve -) { - if (isDef(hash)) { - if (hasOwn(hash, key)) { - res[key] = hash[key]; - if (!preserve) { - delete hash[key]; - } - return true - } else if (hasOwn(hash, altKey)) { - res[key] = hash[altKey]; - if (!preserve) { - delete hash[altKey]; - } - return true - } - } - return false -} - -/* */ - -// The template compiler attempts to minimize the need for normalization by -// statically analyzing the template at compile time. -// -// For plain HTML markup, normalization can be completely skipped because the -// generated render function is guaranteed to return Array. There are -// two cases where extra normalization is needed: - -// 1. When the children contains components - because a functional component -// may return an Array instead of a single root. In this case, just a simple -// normalization is needed - if any child is an Array, we flatten the whole -// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep -// because functional components already normalize their own children. -function simpleNormalizeChildren (children) { - for (var i = 0; i < children.length; i++) { - if (Array.isArray(children[i])) { - return Array.prototype.concat.apply([], children) - } - } - return children -} - -// 2. When the children contains constructs that always generated nested Arrays, -// e.g.