@jvs-milkdown/components 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,10 +7,17 @@ import {
7
7
  addRowAfterCommand,
8
8
  addRowBeforeCommand,
9
9
  deleteSelectedCellsCommand,
10
+ mergeCellsCommand,
10
11
  selectColCommand,
11
12
  selectRowCommand,
12
13
  setAlignCommand,
13
14
  } from '@jvs-milkdown/preset-gfm'
15
+ import { TextSelection } from '@jvs-milkdown/prose/state'
16
+ import {
17
+ CellSelection,
18
+ selectedRect,
19
+ splitCellWithType,
20
+ } from '@jvs-milkdown/prose/tables'
14
21
 
15
22
  import type { Refs } from './types'
16
23
 
@@ -19,67 +26,43 @@ export function useOperation(
19
26
  ctx?: Ctx,
20
27
  getPos?: () => number | undefined
21
28
  ) {
22
- const {
23
- xLineHandleRef,
24
- contentWrapperRef,
25
- colHandleRef,
26
- rowHandleRef,
27
- hoverIndex,
28
- lineHoverIndex,
29
- } = refs
30
-
31
- const onAddRow = () => {
32
- if (!ctx) return
33
- const xHandle = xLineHandleRef.value
34
- if (!xHandle) return
29
+ const { contentWrapperRef, hoverIndex } = refs
35
30
 
36
- const [rowIndex] = lineHoverIndex.value!
37
- if (rowIndex < 0) return
38
-
39
- if (!ctx.get(editorViewCtx).editable) return
31
+ const addRowByIndex = (index: number) => {
32
+ if (!ctx || !ctx.get(editorViewCtx).editable) return
40
33
 
41
34
  const rows = Array.from(
42
35
  contentWrapperRef.value?.querySelectorAll('tr') ?? []
43
36
  )
44
37
  const commands = ctx.get(commandsCtx)
45
38
  const pos = (getPos?.() ?? 0) + 1
46
- if (rows.length === rowIndex) {
47
- commands.call(selectRowCommand.key, { pos, index: rowIndex - 1 })
39
+ if (rows.length === index) {
40
+ commands.call(selectRowCommand.key, { pos, index: index - 1 })
48
41
  commands.call(addRowAfterCommand.key)
49
42
  } else {
50
- commands.call(selectRowCommand.key, { pos, index: rowIndex })
43
+ commands.call(selectRowCommand.key, { pos, index })
51
44
  commands.call(addRowBeforeCommand.key)
52
45
  }
53
46
 
54
- commands.call(selectRowCommand.key, { pos, index: rowIndex })
55
- xHandle.dataset.show = 'false'
47
+ commands.call(selectRowCommand.key, { pos, index })
56
48
  }
57
49
 
58
- const onAddCol = () => {
59
- if (!ctx) return
60
- const xHandle = xLineHandleRef.value
61
- if (!xHandle) return
62
-
63
- const [_, colIndex] = lineHoverIndex.value!
64
- if (colIndex < 0) return
65
-
66
- if (!ctx.get(editorViewCtx).editable) return
67
-
50
+ const addColByIndex = (index: number) => {
51
+ if (!ctx || !ctx.get(editorViewCtx).editable) return
68
52
  const cols = Array.from(
69
53
  contentWrapperRef.value?.querySelector('tr')?.children ?? []
70
54
  )
71
55
  const commands = ctx.get(commandsCtx)
72
56
 
73
57
  const pos = (getPos?.() ?? 0) + 1
74
- if (cols.length === colIndex) {
75
- commands.call(selectColCommand.key, { pos, index: colIndex - 1 })
58
+ if (cols.length === index) {
59
+ commands.call(selectColCommand.key, { pos, index: index - 1 })
76
60
  commands.call(addColAfterCommand.key)
77
61
  } else {
78
- commands.call(selectColCommand.key, { pos, index: colIndex })
62
+ commands.call(selectColCommand.key, { pos, index })
79
63
  commands.call(addColBeforeCommand.key)
80
64
  }
81
-
82
- commands.call(selectColCommand.key, { pos, index: colIndex })
65
+ commands.call(selectColCommand.key, { pos, index })
83
66
  }
84
67
 
85
68
  const selectCol = () => {
@@ -88,11 +71,6 @@ export function useOperation(
88
71
  const commands = ctx.get(commandsCtx)
89
72
  const pos = (getPos?.() ?? 0) + 1
90
73
  commands.call(selectColCommand.key, { pos, index: colIndex })
91
- const buttonGroup =
92
- colHandleRef.value?.querySelector<HTMLElement>('.button-group')
93
- if (buttonGroup)
94
- buttonGroup.dataset.show =
95
- buttonGroup.dataset.show === 'true' ? 'false' : 'true'
96
74
  }
97
75
 
98
76
  const selectRow = () => {
@@ -101,11 +79,6 @@ export function useOperation(
101
79
  const commands = ctx.get(commandsCtx)
102
80
  const pos = (getPos?.() ?? 0) + 1
103
81
  commands.call(selectRowCommand.key, { pos, index: rowIndex })
104
- const buttonGroup =
105
- rowHandleRef.value?.querySelector<HTMLElement>('.button-group')
106
- if (buttonGroup && rowIndex > 0)
107
- buttonGroup.dataset.show =
108
- buttonGroup.dataset.show === 'true' ? 'false' : 'true'
109
82
  }
110
83
 
111
84
  const deleteSelected = (e: PointerEvent) => {
@@ -137,12 +110,98 @@ export function useOperation(
137
110
  })
138
111
  }
139
112
 
113
+ const onMergeCells = (e: PointerEvent) => {
114
+ if (!ctx) return
115
+ if (!ctx.get(editorViewCtx).editable) return
116
+ e.preventDefault()
117
+ e.stopPropagation()
118
+ const commands = ctx.get(commandsCtx)
119
+ commands.call(mergeCellsCommand.key)
120
+ requestAnimationFrame(() => {
121
+ ctx.get(editorViewCtx).focus()
122
+ })
123
+ }
124
+
125
+ const onSplitCell = (e: PointerEvent) => {
126
+ if (!ctx) return
127
+ if (!ctx.get(editorViewCtx).editable) return
128
+ e.preventDefault()
129
+ e.stopPropagation()
130
+ const view = ctx.get(editorViewCtx)
131
+ const { state } = view
132
+ const { selection } = state
133
+
134
+ // Find the merged cell
135
+ let cellNode: ReturnType<typeof selection.$from.node> | null = null
136
+ let cellPos: number | null = null
137
+
138
+ if (selection instanceof CellSelection) {
139
+ selection.forEachCell((cell, pos) => {
140
+ if (
141
+ !cellNode &&
142
+ ((cell.attrs.rowspan ?? 1) > 1 || (cell.attrs.colspan ?? 1) > 1)
143
+ ) {
144
+ cellNode = cell
145
+ cellPos = pos
146
+ }
147
+ })
148
+ } else {
149
+ const { $from } = selection
150
+ for (let d = $from.depth; d > 0; d--) {
151
+ const n = $from.node(d)
152
+ if (n.type.name === 'table_cell' || n.type.name === 'table_header') {
153
+ cellNode = n
154
+ cellPos = $from.before(d)
155
+ break
156
+ }
157
+ }
158
+ }
159
+
160
+ if (!cellNode || cellPos == null) return
161
+ if (
162
+ (cellNode.attrs.colspan ?? 1) == 1 &&
163
+ (cellNode.attrs.rowspan ?? 1) == 1
164
+ )
165
+ return
166
+
167
+ // Set selection to TextSelection inside the merged cell.
168
+ // This avoids the prosemirror-tables bug where splitCell creates
169
+ // a CellSelection using unmapped positions after the split.
170
+ const $cellPos = state.doc.resolve(cellPos + 2)
171
+ view.dispatch(state.tr.setSelection(TextSelection.near($cellPos)))
172
+
173
+ // Use splitCellWithType to produce correct cell types per row.
174
+ // prosemirror-tables' splitCell always uses the merged cell's type
175
+ // (e.g. table_header) for ALL new cells, but milkdown's schema
176
+ // requires table_header in table_header_row and table_cell in
177
+ // table_row. Without this, inserts into body rows fail due to
178
+ // schema mismatch, corrupting the table.
179
+ const newState = view.state
180
+ const { table } = selectedRect(newState)
181
+ const { schema } = newState
182
+
183
+ splitCellWithType(({ row }) => {
184
+ const rowNode = table.child(row)
185
+ return rowNode.type.name === 'table_header_row'
186
+ ? schema.nodes.table_header!
187
+ : schema.nodes.table_cell!
188
+ })(newState, (tr) => {
189
+ view.dispatch(tr)
190
+ })
191
+
192
+ requestAnimationFrame(() => {
193
+ view.focus()
194
+ })
195
+ }
196
+
140
197
  return {
141
- onAddRow,
142
- onAddCol,
198
+ addRowByIndex,
199
+ addColByIndex,
143
200
  selectCol,
144
201
  selectRow,
145
202
  deleteSelected,
146
203
  onAlign,
204
+ onMergeCells,
205
+ onSplitCell,
147
206
  }
148
207
  }
@@ -1,162 +1,10 @@
1
1
  import type { EditorView } from '@jvs-milkdown/prose/view'
2
2
 
3
- import { computePosition, offset } from '@floating-ui/dom'
4
- import { throttle } from 'lodash-es'
5
-
6
3
  import type { Refs } from './types'
7
4
 
8
- import {
9
- computeColHandlePositionByIndex,
10
- computeRowHandlePositionByIndex,
11
- findPointerIndex,
12
- getRelatedDOM,
13
- } from './utils'
14
-
15
- function createPointerMoveHandler(
16
- refs: Refs,
17
- view?: EditorView
18
- ): (e: PointerEvent) => void {
19
- return throttle((e: PointerEvent) => {
20
- if (!view?.editable) return
21
- const {
22
- contentWrapperRef,
23
- yLineHandleRef,
24
- xLineHandleRef,
25
- colHandleRef,
26
- rowHandleRef,
27
- hoverIndex,
28
- lineHoverIndex,
29
- } = refs
30
- const yHandle = yLineHandleRef.value
31
- if (!yHandle) return
32
- const xHandle = xLineHandleRef.value
33
- if (!xHandle) return
34
- const content = contentWrapperRef.value
35
- if (!content) return
36
- const rowHandle = rowHandleRef.value
37
- if (!rowHandle) return
38
- const colHandle = colHandleRef.value
39
- if (!colHandle) return
40
-
41
- const index = findPointerIndex(e, view)
42
- if (!index) return
43
-
44
- const dom = getRelatedDOM(contentWrapperRef, index)
45
- if (!dom) return
46
-
47
- const [rowIndex, colIndex] = index
48
- const boundary = dom.col.getBoundingClientRect()
49
- const closeToBoundaryLeft = Math.abs(e.clientX - boundary.left) < 8
50
- const closeToBoundaryRight = Math.abs(boundary.right - e.clientX) < 8
51
- const closeToBoundaryTop = Math.abs(e.clientY - boundary.top) < 8
52
- const closeToBoundaryBottom = Math.abs(boundary.bottom - e.clientY) < 8
53
-
54
- const closeToBoundary =
55
- closeToBoundaryLeft ||
56
- closeToBoundaryRight ||
57
- closeToBoundaryTop ||
58
- closeToBoundaryBottom
59
-
60
- const rowButtonGroup = rowHandle.querySelector<HTMLElement>('.button-group')
61
- const colButtonGroup = colHandle.querySelector<HTMLElement>('.button-group')
62
- if (rowButtonGroup) rowButtonGroup.dataset.show = 'false'
63
- if (colButtonGroup) colButtonGroup.dataset.show = 'false'
64
-
65
- if (closeToBoundary) {
66
- const contentBoundary = content.getBoundingClientRect()
67
- rowHandle.dataset.show = 'false'
68
- colHandle.dataset.show = 'false'
69
- xHandle.dataset.displayType = 'tool'
70
- yHandle.dataset.displayType = 'tool'
71
-
72
- const yHandleWidth = yHandle.getBoundingClientRect().width
73
- const xHandleHeight = xHandle.getBoundingClientRect().height
74
-
75
- // display vertical line handle
76
- if (closeToBoundaryLeft || closeToBoundaryRight) {
77
- lineHoverIndex.value![1] = closeToBoundaryLeft ? colIndex : colIndex + 1
78
- computePosition(dom.col, yHandle, {
79
- placement: closeToBoundaryLeft ? 'left' : 'right',
80
- middleware: [offset(closeToBoundaryLeft ? -1 * yHandleWidth : 0)],
81
- })
82
- .then(({ x }) => {
83
- yHandle.dataset.show = 'true'
84
- Object.assign(yHandle.style, {
85
- height: `${contentBoundary.height}px`,
86
- left: `${x}px`,
87
- })
88
- })
89
- .catch(console.error)
90
- } else {
91
- yHandle.dataset.show = 'false'
92
- }
93
-
94
- // display horizontal line handle
95
- // won't display if the row is the header row
96
- if (index[0] !== 0 && (closeToBoundaryTop || closeToBoundaryBottom)) {
97
- lineHoverIndex.value![0] = closeToBoundaryTop ? rowIndex : rowIndex + 1
98
- computePosition(dom.row, xHandle, {
99
- placement: closeToBoundaryTop ? 'top' : 'bottom',
100
- middleware: [offset(closeToBoundaryTop ? -1 * xHandleHeight : 0)],
101
- })
102
- .then(({ y }) => {
103
- xHandle.dataset.show = 'true'
104
- Object.assign(xHandle.style, {
105
- width: `${contentBoundary.width}px`,
106
- top: `${y}px`,
107
- })
108
- })
109
- .catch(console.error)
110
- } else {
111
- xHandle.dataset.show = 'false'
112
- }
113
-
114
- return
115
- }
116
-
117
- lineHoverIndex.value = [-1, -1]
118
-
119
- yHandle.dataset.show = 'false'
120
- xHandle.dataset.show = 'false'
121
- rowHandle.dataset.show = 'true'
122
- colHandle.dataset.show = 'true'
123
-
124
- computeRowHandlePositionByIndex({
125
- refs,
126
- index,
127
- })
128
- computeColHandlePositionByIndex({
129
- refs,
130
- index,
131
- })
132
- hoverIndex.value = index
133
- }, 20)
134
- }
135
-
136
- function createPointerLeaveHandler(refs: Refs): () => void {
137
- return () => {
138
- const { rowHandleRef, colHandleRef, yLineHandleRef, xLineHandleRef } = refs
139
- setTimeout(() => {
140
- const rowHandle = rowHandleRef.value
141
- if (!rowHandle) return
142
- const colHandle = colHandleRef.value
143
- if (!colHandle) return
144
- const yHandle = yLineHandleRef.value
145
- if (!yHandle) return
146
- const xHandle = xLineHandleRef.value
147
- if (!xHandle) return
148
-
149
- rowHandle.dataset.show = 'false'
150
- colHandle.dataset.show = 'false'
151
- yHandle.dataset.show = 'false'
152
- xHandle.dataset.show = 'false'
153
- }, 200)
154
- }
155
- }
156
-
157
- export function usePointerHandlers(refs: Refs, view?: EditorView) {
158
- const pointerMove = createPointerMoveHandler(refs, view)
159
- const pointerLeave = createPointerLeaveHandler(refs)
5
+ export function usePointerHandlers(_refs: Refs, _view?: EditorView) {
6
+ const pointerMove = (_e: PointerEvent) => {}
7
+ const pointerLeave = () => {}
160
8
 
161
9
  return {
162
10
  pointerMove,
@@ -17,18 +17,12 @@ export interface DragContext {
17
17
  contentRoot: HTMLTableSectionElement
18
18
  yHandle: HTMLDivElement
19
19
  xHandle: HTMLDivElement
20
- colHandle: HTMLDivElement
21
- rowHandle: HTMLDivElement
22
20
  }
23
21
 
24
22
  export interface Refs {
25
23
  dragPreviewRef: Ref<HTMLDivElement | undefined>
26
24
  tableWrapperRef: Ref<HTMLDivElement | undefined>
27
25
  contentWrapperRef: Ref<HTMLElement | undefined>
28
- yLineHandleRef: Ref<HTMLDivElement | undefined>
29
- xLineHandleRef: Ref<HTMLDivElement | undefined>
30
- colHandleRef: Ref<HTMLDivElement | undefined>
31
- rowHandleRef: Ref<HTMLDivElement | undefined>
32
26
  hoverIndex: Ref<CellIndex>
33
27
  lineHoverIndex: Ref<CellIndex>
34
28
  dragInfo: Ref<DragInfo | undefined>
@@ -2,7 +2,6 @@ import type { Node } from '@jvs-milkdown/prose/model'
2
2
  import type { EditorView } from '@jvs-milkdown/prose/view'
3
3
  import type { Ref } from 'vue'
4
4
 
5
- import { computePosition } from '@floating-ui/dom'
6
5
  import { findParent } from '@jvs-milkdown/prose'
7
6
  import { CellSelection, findTable } from '@jvs-milkdown/prose/tables'
8
7
 
@@ -98,15 +97,7 @@ export function recoveryStateBetweenUpdate(
98
97
  if (selection.isColSelection()) {
99
98
  const { $head } = selection
100
99
  const colIndex = $head.index($head.depth - 1)
101
- computeColHandlePositionByIndex({
102
- refs,
103
- index: [0, colIndex],
104
- before: (handleDOM) => {
105
- handleDOM
106
- .querySelector('.button-group')
107
- ?.setAttribute('data-show', 'true')
108
- },
109
- })
100
+ refs.hoverIndex.value = [0, colIndex]
110
101
  return
111
102
  }
112
103
  if (selection.isRowSelection()) {
@@ -117,76 +108,6 @@ export function recoveryStateBetweenUpdate(
117
108
  )($head)
118
109
  if (!rowNode) return
119
110
  const rowIndex = findNodeIndex(table.node, rowNode.node)
120
- computeRowHandlePositionByIndex({
121
- refs,
122
- index: [rowIndex, 0],
123
- before: (handleDOM) => {
124
- if (rowIndex > 0)
125
- handleDOM
126
- .querySelector('.button-group')
127
- ?.setAttribute('data-show', 'true')
128
- },
129
- })
111
+ refs.hoverIndex.value = [rowIndex, 0]
130
112
  }
131
113
  }
132
-
133
- interface ComputeHandlePositionByIndexProps {
134
- refs: Refs
135
- index: CellIndex
136
- before?: (handleDOM: HTMLDivElement) => void
137
- after?: (handleDOM: HTMLDivElement) => void
138
- }
139
-
140
- export function computeColHandlePositionByIndex({
141
- refs,
142
- index,
143
- before,
144
- after,
145
- }: ComputeHandlePositionByIndexProps) {
146
- const { contentWrapperRef, colHandleRef, hoverIndex } = refs
147
- const colHandle = colHandleRef.value
148
- if (!colHandle) return
149
-
150
- hoverIndex.value = index
151
- const dom = getRelatedDOM(contentWrapperRef, index)
152
- if (!dom) return
153
- const { headerCol: col } = dom
154
- colHandle.dataset.show = 'true'
155
- if (before) before(colHandle)
156
- computePosition(col, colHandle, { placement: 'top' })
157
- .then(({ x, y }) => {
158
- Object.assign(colHandle.style, {
159
- left: `${x}px`,
160
- top: `${y}px`,
161
- })
162
- if (after) after(colHandle)
163
- })
164
- .catch(console.error)
165
- }
166
-
167
- export function computeRowHandlePositionByIndex({
168
- refs,
169
- index,
170
- before,
171
- after,
172
- }: ComputeHandlePositionByIndexProps) {
173
- const { contentWrapperRef, rowHandleRef, hoverIndex } = refs
174
- const rowHandle = rowHandleRef.value
175
- if (!rowHandle) return
176
-
177
- hoverIndex.value = index
178
- const dom = getRelatedDOM(contentWrapperRef, index)
179
- if (!dom) return
180
- const { row } = dom
181
- rowHandle.dataset.show = 'true'
182
- if (before) before(rowHandle)
183
- computePosition(row, rowHandle, { placement: 'left' })
184
- .then(({ x, y }) => {
185
- Object.assign(rowHandle.style, {
186
- left: `${x}px`,
187
- top: `${y}px`,
188
- })
189
- if (after) after(rowHandle)
190
- })
191
- .catch(console.error)
192
- }
@@ -8,11 +8,14 @@ import type {
8
8
  } from '@jvs-milkdown/prose/view'
9
9
 
10
10
  import { tableSchema } from '@jvs-milkdown/preset-gfm'
11
- import { findParent } from '@jvs-milkdown/prose'
12
- import { NodeSelection, TextSelection } from '@jvs-milkdown/prose/state'
13
- import { CellSelection } from '@jvs-milkdown/prose/tables'
14
11
  import { $view } from '@jvs-milkdown/utils'
15
- import { createApp, shallowRef, type App, type ShallowRef } from 'vue'
12
+ import {
13
+ createApp,
14
+ shallowRef,
15
+ triggerRef,
16
+ type App,
17
+ type ShallowRef,
18
+ } from 'vue'
16
19
 
17
20
  import { withMeta } from '../../__internal__/meta'
18
21
  import { tableBlockConfig } from '../config'
@@ -24,6 +27,7 @@ export class TableNodeView implements NodeView {
24
27
  app: App
25
28
 
26
29
  nodeRef: ShallowRef<Node>
30
+ #selectionUpdateScheduled = false
27
31
 
28
32
  constructor(
29
33
  public ctx: Ctx,
@@ -59,8 +63,17 @@ export class TableNodeView implements NodeView {
59
63
  update(node: Node) {
60
64
  if (node.type !== this.node.type) return false
61
65
 
62
- if (node.sameMarkup(this.node) && node.content.eq(this.node.content))
63
- return false
66
+ if (node.sameMarkup(this.node) && node.content.eq(this.node.content)) {
67
+ // Content unchanged — still trigger a reactive tick for selection-based UI (cell toolbar)
68
+ if (!this.#selectionUpdateScheduled) {
69
+ this.#selectionUpdateScheduled = true
70
+ queueMicrotask(() => {
71
+ triggerRef(this.nodeRef)
72
+ this.#selectionUpdateScheduled = false
73
+ })
74
+ }
75
+ return true
76
+ }
64
77
 
65
78
  this.node = node
66
79
  this.nodeRef.value = node
@@ -68,63 +81,11 @@ export class TableNodeView implements NodeView {
68
81
  return true
69
82
  }
70
83
 
71
- #handleClick(event: PointerEvent) {
72
- const view = this.view
73
- if (!view.editable) return false
74
-
75
- const { state, dispatch } = view
76
- const pos = view.posAtCoords({ left: event.clientX, top: event.clientY })
77
-
78
- if (!pos) return false
79
-
80
- const $pos = state.doc.resolve(pos.inside)
81
- const node = findParent(
82
- (node) =>
83
- node.type.name === 'table_cell' || node.type.name === 'table_header'
84
- )($pos)
85
-
86
- if (!node) return false
87
-
88
- // if the selection is a text selection, and the current node is the same as the node, return false
89
- if (state.selection instanceof TextSelection) {
90
- const currentNode = findParent(
91
- (node) =>
92
- node.type.name === 'table_cell' || node.type.name === 'table_header'
93
- )(state.selection.$from)
94
- if (currentNode?.node === node.node) return false
95
- }
96
-
97
- const { from } = node
98
-
99
- const selection = NodeSelection.create(state.doc, from + 1)
100
- if (state.selection.eq(selection)) return false
101
-
102
- if (state.selection instanceof CellSelection) {
103
- setTimeout(() => {
104
- dispatch(state.tr.setSelection(selection).scrollIntoView())
105
- }, 20)
106
- } else {
107
- requestAnimationFrame(() => {
108
- dispatch(state.tr.setSelection(selection).scrollIntoView())
109
- })
110
- }
111
- return true
112
- }
113
-
114
84
  stopEvent(e: Event) {
115
85
  if (e.type === 'drop' || e.type.startsWith('drag')) return true
116
86
 
117
87
  if (e.type === 'mousedown' || e.type === 'pointerdown') {
118
88
  if (e.target instanceof Element && e.target.closest('button')) return true
119
-
120
- const target = e.target
121
- if (
122
- target instanceof HTMLElement &&
123
- (target.closest('th') || target.closest('td'))
124
- ) {
125
- const event = e as PointerEvent
126
- return this.#handleClick(event)
127
- }
128
89
  }
129
90
 
130
91
  return false
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2020-present Mirone
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.