tiptapify 0.0.29 → 0.0.31

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tiptapify",
3
3
  "types": "./index.d.ts",
4
- "version": "0.0.29",
4
+ "version": "0.0.31",
5
5
  "description": "Tiptap3 editor with Vuetify3 menu implementation",
6
6
  "exports": {
7
7
  ".": {
@@ -59,70 +59,70 @@
59
59
  "repository": "https://github.com/IVoyt/tiptapify",
60
60
  "packageManager": "pnpm@10.15.1",
61
61
  "dependencies": {
62
- "@tiptap/core": "^3.4.1",
63
- "@tiptap/extension-blockquote": "^3.4.1",
64
- "@tiptap/extension-bold": "^3.4.1",
65
- "@tiptap/extension-bubble-menu": "^3.4.1",
66
- "@tiptap/extension-code": "^3.4.1",
67
- "@tiptap/extension-code-block": "^3.4.1",
68
- "@tiptap/extension-code-block-lowlight": "^3.4.1",
69
- "@tiptap/extension-color": "^3.4.1",
70
- "@tiptap/extension-document": "^3.4.1",
71
- "@tiptap/extension-floating-menu": "^3.4.1",
72
- "@tiptap/extension-font-family": "^3.4.1",
73
- "@tiptap/extension-hard-break": "^3.4.1",
74
- "@tiptap/extension-heading": "^3.4.1",
75
- "@tiptap/extension-highlight": "^3.4.1",
76
- "@tiptap/extension-horizontal-rule": "^3.4.1",
77
- "@tiptap/extension-image": "^3.4.1",
78
- "@tiptap/extension-invisible-characters": "^3.4.1",
79
- "@tiptap/extension-italic": "^3.4.1",
80
- "@tiptap/extension-link": "^3.4.1",
81
- "@tiptap/extension-list": "^3.4.1",
82
- "@tiptap/extension-list-item": "^3.4.1",
83
- "@tiptap/extension-paragraph": "^3.4.1",
84
- "@tiptap/extension-placeholder": "^3.4.1",
85
- "@tiptap/extension-strike": "^3.4.1",
86
- "@tiptap/extension-subscript": "^3.4.1",
87
- "@tiptap/extension-superscript": "^3.4.1",
88
- "@tiptap/extension-table": "^3.4.1",
89
- "@tiptap/extension-task-item": "^3.4.1",
90
- "@tiptap/extension-task-list": "^3.4.1",
91
- "@tiptap/extension-text": "^3.4.1",
92
- "@tiptap/extension-text-align": "^3.4.1",
93
- "@tiptap/extension-typography": "^3.4.1",
94
- "@tiptap/extension-underline": "^3.4.1",
95
- "@tiptap/extension-youtube": "^3.4.1",
96
- "@tiptap/extensions": "^3.4.1",
97
- "@tiptap/pm": "^3.4.1",
98
- "@tiptap/suggestion": "^3.4.1",
99
- "@tiptap/vue-3": "^3.4.1",
62
+ "@tiptap/core": "^3.20.0",
63
+ "@tiptap/extension-blockquote": "^3.20.0",
64
+ "@tiptap/extension-bold": "^3.20.0",
65
+ "@tiptap/extension-bubble-menu": "^3.20.0",
66
+ "@tiptap/extension-code": "^3.20.0",
67
+ "@tiptap/extension-code-block": "^3.20.0",
68
+ "@tiptap/extension-code-block-lowlight": "^3.20.0",
69
+ "@tiptap/extension-color": "^3.20.0",
70
+ "@tiptap/extension-document": "^3.20.0",
71
+ "@tiptap/extension-floating-menu": "^3.20.0",
72
+ "@tiptap/extension-font-family": "^3.20.0",
73
+ "@tiptap/extension-hard-break": "^3.20.0",
74
+ "@tiptap/extension-heading": "^3.20.0",
75
+ "@tiptap/extension-highlight": "^3.20.0",
76
+ "@tiptap/extension-horizontal-rule": "^3.20.0",
77
+ "@tiptap/extension-image": "^3.20.0",
78
+ "@tiptap/extension-invisible-characters": "^3.20.0",
79
+ "@tiptap/extension-italic": "^3.20.0",
80
+ "@tiptap/extension-link": "^3.20.0",
81
+ "@tiptap/extension-list": "^3.20.0",
82
+ "@tiptap/extension-list-item": "^3.20.0",
83
+ "@tiptap/extension-paragraph": "^3.20.0",
84
+ "@tiptap/extension-placeholder": "^3.20.0",
85
+ "@tiptap/extension-strike": "^3.20.0",
86
+ "@tiptap/extension-subscript": "^3.20.0",
87
+ "@tiptap/extension-superscript": "^3.20.0",
88
+ "@tiptap/extension-table": "^3.20.0",
89
+ "@tiptap/extension-task-item": "^3.20.0",
90
+ "@tiptap/extension-task-list": "^3.20.0",
91
+ "@tiptap/extension-text": "^3.20.0",
92
+ "@tiptap/extension-text-align": "^3.20.0",
93
+ "@tiptap/extension-typography": "^3.20.0",
94
+ "@tiptap/extension-underline": "^3.20.0",
95
+ "@tiptap/extension-youtube": "^3.20.0",
96
+ "@tiptap/extensions": "^3.20.0",
97
+ "@tiptap/pm": "^3.20.0",
98
+ "@tiptap/suggestion": "^3.20.0",
99
+ "@tiptap/vue-3": "^3.20.0",
100
100
  "emoji.json": "^16.0.0",
101
101
  "highlight.js": "^11.11.1",
102
102
  "linkifyjs": "^4.3.2",
103
103
  "lowlight": "^3.3.0",
104
- "vue-i18n": "^11.1.12"
104
+ "vue-i18n": "^11.2.8"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@mdi/js": "^7.4.47",
108
108
  "vue": "^3.5.14",
109
- "vuetify": "^3.8.5"
109
+ "vuetify": "^3.8.5 || ^4.0.0"
110
110
  },
111
111
  "devDependencies": {
112
112
  "@intlify/unplugin-vue-i18n": "^6.0.8",
113
113
  "@rollup/plugin-alias": "^5.1.1",
114
- "@types/node": "^22.18.1",
114
+ "@types/node": "^22.19.13",
115
115
  "@vitejs/plugin-vue": "^5.2.4",
116
116
  "@vitejs/plugin-vue-jsx": "^4.2.0",
117
117
  "rollup-plugin-tsconfig-paths": "^1.5.2",
118
- "sass-embedded": "^1.92.0",
119
- "tsx": "^4.20.5",
120
- "typescript": "^5.9.2",
118
+ "sass-embedded": "^1.97.3",
119
+ "tsx": "^4.21.0",
120
+ "typescript": "^5.9.3",
121
121
  "unplugin-vue-components": "^28.8.0",
122
- "vite": "^6.3.5",
123
- "vite-plugin-vuetify": "^2.1.2",
122
+ "vite": "^6.4.1",
123
+ "vite-plugin-vuetify": "^2.1.3",
124
124
  "vite-svg-loader": "^5.1.0",
125
125
  "vite-tsconfig-paths": "^5.1.4",
126
- "vue-tsc": "^3.0.0"
126
+ "vue-tsc": "^3.2.5"
127
127
  }
128
128
  }
@@ -1,6 +1,7 @@
1
1
  import { default as EmojiButton } from "@tiptapify/extensions/components/media/emoji/Button.vue";
2
2
  import { default as LinkButton } from "@tiptapify/extensions/components/media/link/Button.vue";
3
3
  import { default as ImageButton } from "@tiptapify/extensions/components/media/image/Button.vue";
4
+ import { default as IframeButton } from "@tiptapify/extensions/components/media/iframe/Button.vue";
4
5
  import { default as TableButton } from "@tiptapify/extensions/components/media/table/Button.vue";
5
6
  import { default as VideoButton } from "@tiptapify/extensions/components/media/video/Button.vue";
6
7
  import { markRaw } from "vue";
@@ -21,6 +22,10 @@ export default {
21
22
  name: 'video',
22
23
  component: markRaw(VideoButton),
23
24
  },
25
+ {
26
+ name: 'iframe',
27
+ component: markRaw(IframeButton),
28
+ },
24
29
  {
25
30
  name: 'emoji',
26
31
  component: markRaw(EmojiButton),
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
 
3
3
  import * as mdi from "@mdi/js";
4
- import { inject, nextTick, ref, watch } from "vue";
4
+ import { inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
5
5
 
6
6
  const props = defineProps({
7
7
  module: String,
@@ -100,6 +100,42 @@ watch(() => dialog.value, async () => {
100
100
  dragElement(movableHandler.value.$el as HTMLElement, movableHandler.value.$el.parentNode as HTMLElement)
101
101
  }
102
102
  })
103
+
104
+ function resizeListener() {
105
+ if (!dialog.value) {
106
+ return
107
+ }
108
+
109
+ const dialogCoordinates = movableHandler.value.$el.parentNode.getBoundingClientRect();
110
+
111
+ function checkBoundLeftTop(type) {
112
+ if (dialogCoordinates[type] < 0) {
113
+ const dialogOffset = movableHandler.value.$el.parentNode.style[type]
114
+ const newOffset = parseInt(dialogOffset) - dialogCoordinates[type]
115
+ movableHandler.value.$el.parentNode.style[type] = `${newOffset}px`
116
+ }
117
+ }
118
+ checkBoundLeftTop('left')
119
+ checkBoundLeftTop('top')
120
+
121
+ function checkBoundRightBottom(type: 'right' | 'bottom') {
122
+ const data = { side: type === 'right' ? 'left' : 'top', dim: type === 'right' ? 'width' : 'height', windowDim: type === 'right' ? 'innerWidth' : 'innerHeight' }
123
+ if ((dialogCoordinates[data.side] + dialogCoordinates[data.dim]) > window[data.windowDim]) {
124
+ const newOffset = ((window[data.windowDim] - dialogCoordinates[data.dim]) / 2).toFixed()
125
+ movableHandler.value.$el.parentNode.style[data.side] = `${newOffset}px`
126
+ }
127
+ }
128
+ checkBoundRightBottom('right')
129
+ checkBoundRightBottom('bottom')
130
+ }
131
+
132
+ onMounted(() => {
133
+ addEventListener('resize', resizeListener)
134
+ })
135
+
136
+ onBeforeUnmount(() => {
137
+ removeEventListener('resize', resizeListener)
138
+ })
103
139
  </script>
104
140
 
105
141
  <template>
@@ -25,9 +25,10 @@ import { TableKit } from '@tiptap/extension-table'
25
25
  import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
26
26
  import { InvisibleCharacters } from '@tiptap/extension-invisible-characters'
27
27
 
28
+ import { default as Iframe } from '@tiptapify/extensions/components/media/iframe'
28
29
  import { BulletListCircle, BulletListSquare } from '@tiptapify/extensions/components/list/bullet'
29
- import { TiptapifyLink } from '@tiptapify/extensions/components/media/link'
30
30
  import { TiptapifyImage } from '@tiptapify/extensions/components/media/image'
31
+ import { TiptapifyLink } from '@tiptapify/extensions/components/media/link'
31
32
  import { TiptapifyVideo } from '@tiptapify/extensions/components/media/video'
32
33
  import CodeBlockComponent from '@tiptapify/extensions/components/CodeBlockComponent.vue'
33
34
  import SlashCommands from '@tiptapify/extensions/slash-commands'
@@ -84,6 +85,7 @@ export function editorExtensions (placeholder: string, slashCommands: boolean, c
84
85
  }),
85
86
  TiptapifyImage,
86
87
  TiptapifyVideo,
88
+ Iframe,
87
89
  Superscript,
88
90
  Subscript,
89
91
  TableKit,
@@ -0,0 +1,42 @@
1
+ <script lang="ts" setup>
2
+
3
+ import { Editor } from "@tiptap/vue-3";
4
+ import BtnIcon from "@tiptapify/components/UI/BtnIcon.vue";
5
+ import IframeDialog from "@tiptapify/extensions/components/media/iframe/IframeDialog.vue";
6
+ import { inject, ref, Ref } from "vue";
7
+
8
+ import defaults from '@tiptapify/constants/defaults'
9
+
10
+ defineProps({
11
+ variantBtn: { type: String, default: defaults.variantBtn }
12
+ })
13
+
14
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
15
+
16
+ const { t } = inject('tiptapifyI18n') as any
17
+
18
+ const dialog = ref(null)
19
+
20
+ const icon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M1 12h9.8L8.3 9.5l1.4-1.4l4.9 4.9l-4.9 4.9l-1.4-1.4l2.5-2.5H1zM21 2H3c-1.1 0-2 .9-2 2v6.1h2V6h18v14H3v-4H1v4c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2"/></svg>'
21
+ </script>
22
+
23
+ <template>
24
+ <VBtn
25
+ :color="editor.isActive('iframe') ? 'primary' : ''"
26
+ :variant="variantBtn"
27
+ @click="dialog?.open()"
28
+ size="32"
29
+ >
30
+ <VTooltip activator="parent">
31
+ {{ t('media.iframe') }}
32
+ </VTooltip>
33
+ <BtnIcon :icon="icon" />
34
+ </VBtn>
35
+
36
+ <IframeDialog ref="dialog" />
37
+ <!-- Dialog here -->
38
+ </template>
39
+
40
+ <style lang="scss" scoped>
41
+
42
+ </style>
@@ -0,0 +1,192 @@
1
+ <script setup lang="ts">
2
+
3
+ import { Editor } from "@tiptap/vue-3";
4
+ import TiptapifyDialog from "@tiptapify/components/UI/TiptapifyDialog.vue";
5
+ import defaults from "@tiptapify/constants/defaults";
6
+
7
+ import type { iframeOptions } from '@tiptapify/types/iframe'
8
+
9
+ import { computed, inject, Ref, ref, watch } from 'vue'
10
+
11
+ defineProps({
12
+ variantBtn: { type: String, default() { return defaults.variantBtn }},
13
+ variantField: { type: String, default() { return defaults.variantField }}
14
+ })
15
+
16
+ const editor = inject('tiptapifyEditor') as Ref<Editor>
17
+ const { t } = inject('tiptapifyI18n') as any
18
+
19
+ const generateIframeAttrs = (): iframeOptions => ({
20
+ src: '',
21
+ name: '',
22
+ title: '',
23
+ height: null,
24
+ width: null,
25
+ frameborder: 0,
26
+ })
27
+
28
+ const attrs = ref(generateIframeAttrs())
29
+ const srcInvalid = ref(false)
30
+
31
+ const dialog = ref(null)
32
+
33
+ const isDisabled = computed(() => {
34
+ const { src } = attrs.value
35
+ return !src
36
+ })
37
+
38
+ function apply() {
39
+ let { src, name, title, width, height, frameborder } = attrs.value
40
+
41
+ const options: iframeOptions = {
42
+ src,
43
+ name,
44
+ title,
45
+ }
46
+
47
+ if (width) {
48
+ options.width = width
49
+ }
50
+
51
+ if (height) {
52
+ options.height = height
53
+ }
54
+
55
+ if (frameborder) {
56
+ options.frameborder = Number(frameborder)
57
+ }
58
+
59
+ if (src) {
60
+ editor.value.chain().focus().setIframe(options).run()
61
+ }
62
+
63
+ close()
64
+ }
65
+
66
+ function clear() {
67
+ editor.value.commands.deleteSelection()
68
+
69
+ close()
70
+ }
71
+
72
+ function close() {
73
+ dialog.value.close()
74
+
75
+ attrs.value = generateIframeAttrs()
76
+ }
77
+
78
+ function open () {
79
+ const iframe = editor.value.getAttributes('iframe')
80
+
81
+ attrs.value.src = iframe?.src
82
+ attrs.value.name = iframe?.name
83
+ attrs.value.title = iframe?.title
84
+ attrs.value.width = iframe?.width
85
+ attrs.value.height = iframe?.height
86
+ attrs.value.frameborder = !!iframe?.frameborder
87
+
88
+ dialog.value.open()
89
+ }
90
+
91
+ defineExpose({ open })
92
+
93
+ watch(() => attrs.value.src, () => {
94
+ const azAZ09 = 'a-zA-Z0-9'
95
+
96
+ const regexHrefProto = 'https?:\\/\\/'
97
+ const regexHrefDomain = `[${azAZ09}]+(\\.[${azAZ09}]+(-?[${azAZ09}]+)?)+`
98
+ const regexHrefPath = `(\\/[${azAZ09}\\-]+)*`
99
+ const regexHrefFragment = '(#[^\\s]*)?'
100
+ const regexHrefQueryParam = `(\\?[${azAZ09}\\-_]+((\\[[${azAZ09}]+\\])?=[${azAZ09}\\-_%]+)?)?`
101
+ const regexHrefQueryParamExtra = `(&[${azAZ09}\\-_]+((\\[[${azAZ09}]+\\])?=[${azAZ09}\\-_%]+)?)*`
102
+ const regexHref = `${regexHrefProto}${regexHrefDomain}${regexHrefPath}${regexHrefFragment}${regexHrefQueryParam}${regexHrefQueryParamExtra}`
103
+
104
+ const regex = new RegExp(`^(${regexHref})$`, 'i')
105
+
106
+ srcInvalid.value = (attrs.value.src || '') !== '' && !regex.test(attrs.value.src)
107
+ })
108
+ </script>
109
+
110
+ <template>
111
+ <TiptapifyDialog ref="dialog" module="iframe" :title="t('dialog.iframe.dialog_title')" :max-width="800">
112
+ <template #content>
113
+ <VCardText>
114
+ <VRow>
115
+ <VCol cols="12">
116
+ <VTextField
117
+ v-model="attrs.src"
118
+ density="compact"
119
+ variant="outlined"
120
+ :label="t('dialog.iframe.src')"
121
+ :error-messages="srcInvalid ? t('dialog.iframe.src_invalid') : ''"
122
+ />
123
+ </VCol>
124
+
125
+ <VCol cols="12" md="6">
126
+ <VRow>
127
+ <VCol cols="12">
128
+ <VTextField v-model="attrs.name" density="compact" variant="outlined" :label="t('dialog.iframe.name')" />
129
+ </VCol>
130
+
131
+ <VCol cols="12">
132
+ <VTextField v-model="attrs.title" density="compact" variant="outlined" :label="t('dialog.iframe.title')" />
133
+ </VCol>
134
+ </VRow>
135
+ </VCol>
136
+
137
+ <VCol cols="12" md="6">
138
+ <VRow>
139
+ <VCol cols="12" md="6">
140
+ <VTextField
141
+ v-model="attrs.width"
142
+ type="number"
143
+ density="compact"
144
+ variant="outlined"
145
+ :precision="0"
146
+ :min="1"
147
+ :label="t('dialog.iframe.width')"
148
+ />
149
+ </VCol>
150
+
151
+ <VCol cols="12" md="6">
152
+ <VTextField
153
+ v-model="attrs.height"
154
+ type="number"
155
+ density="compact"
156
+ variant="outlined"
157
+ :precision="0"
158
+ :min="1"
159
+ :label="t('dialog.iframe.height')"
160
+ />
161
+ </VCol>
162
+
163
+ <VCol cols="12" class="ml-2">
164
+ <VSwitch v-model="attrs.frameborder" density="compact" :hideDetails="true" :label="t('dialog.iframe.frameborder')" />
165
+ </VCol>
166
+ </VRow>
167
+ </VCol>
168
+ </VRow>
169
+ </VCardText>
170
+ </template>
171
+
172
+ <template #actions>
173
+ <VCardActions>
174
+ <VRow>
175
+ <VCol class="d-flex justify-start">
176
+ <VBtn color="warning" v-if="editor.isActive('image')" :variant="variantBtn" :disabled="isDisabled" @click="clear">
177
+ {{ t('dialog.clear') }}
178
+ </VBtn>
179
+ </VCol>
180
+ <VCol class="d-flex justify-end">
181
+ <VBtn :variant="variantBtn" @click="close" class="mr-2">
182
+ {{ t('dialog.close') }}
183
+ </VBtn>
184
+ <VBtn color="primary" :variant="variantBtn" :disabled="isDisabled" @click="apply">
185
+ {{ t('dialog.apply') }}
186
+ </VBtn>
187
+ </VCol>
188
+ </VRow>
189
+ </VCardActions>
190
+ </template>
191
+ </TiptapifyDialog>
192
+ </template>
@@ -0,0 +1,92 @@
1
+ import { Node } from '@tiptap/core'
2
+
3
+ export interface IframeOptions {
4
+ allowFullscreen: boolean
5
+ HTMLAttributes: {
6
+ [key: string]: any
7
+ }
8
+ }
9
+
10
+ declare module '@tiptap/core' {
11
+ interface Commands<ReturnType> {
12
+ iframe: {
13
+ /**
14
+ * Add an iframe
15
+ */
16
+ setIframe: (options: { src: string }) => ReturnType
17
+ }
18
+ }
19
+ }
20
+
21
+ export default Node.create<IframeOptions>({
22
+ name: 'iframe',
23
+
24
+ group: 'block',
25
+
26
+ atom: true,
27
+
28
+ addOptions() {
29
+ return {
30
+ allowFullscreen: true,
31
+ HTMLAttributes: {
32
+ class: 'iframe-wrapper',
33
+ },
34
+ }
35
+ },
36
+
37
+ addAttributes() {
38
+ return {
39
+ src: {
40
+ default: null,
41
+ },
42
+ name: {
43
+ default: null,
44
+ },
45
+ title: {
46
+ default: null,
47
+ },
48
+ width: {
49
+ default: null,
50
+ },
51
+ height: {
52
+ default: null,
53
+ },
54
+ frameborder: {
55
+ default: 0,
56
+ },
57
+ allowfullscreen: {
58
+ default: this.options.allowFullscreen,
59
+ parseHTML: () => this.options.allowFullscreen,
60
+ },
61
+ }
62
+ },
63
+
64
+ parseHTML() {
65
+ return [
66
+ {
67
+ tag: 'iframe',
68
+ },
69
+ ]
70
+ },
71
+
72
+ renderHTML({ HTMLAttributes }) {
73
+ return ['div', this.options.HTMLAttributes, ['iframe', HTMLAttributes]]
74
+ },
75
+
76
+ addCommands() {
77
+ return {
78
+ setIframe:
79
+ (options: { src: string }) =>
80
+ ({ tr, dispatch }) => {
81
+ const { selection } = tr
82
+ const node = this.type.create(options)
83
+
84
+ if (dispatch) {
85
+ tr.replaceRangeWith(selection.from, selection.to, node)
86
+ }
87
+
88
+ return true
89
+ },
90
+ }
91
+ },
92
+ })
@@ -102,7 +102,7 @@ watch(() => attrs.value.href, () => {
102
102
 
103
103
  const regex = new RegExp(`^(${regexAll})$`, 'i')
104
104
 
105
- hrefInvalid.value = attrs.value.href !== '' && !regex.test(attrs.value.href)
105
+ hrefInvalid.value = (attrs.value.href || '') !== '' && !regex.test(attrs.value.href)
106
106
  })
107
107
  </script>
108
108
 
@@ -44,7 +44,7 @@ async function changeEditorContainer(source: string, target: string) {
44
44
  <template>
45
45
  <VBtn @click="dialog ? dialogClose() : dialogOpen()" size="32" :variant="variantBtn">
46
46
  <VTooltip activator="parent">
47
- {{ t('misc.preview') }}
47
+ {{ t('misc.fullscreen') }}
48
48
  </VTooltip>
49
49
  <BtnIcon :icon="dialog ? `mdiSvg:${mdi.mdiFullscreenExit}` : `mdiSvg:${mdi.mdiFullscreen}`" />
50
50
  </VBtn>
@@ -95,6 +95,16 @@
95
95
  "apply": "应用",
96
96
  "clear": "清除",
97
97
  "close": "关闭",
98
+ "iframe": {
99
+ "dialog_title": "添加/编辑内嵌框架",
100
+ "src": "源地址",
101
+ "name": "名称",
102
+ "title": "标题",
103
+ "width": "宽度",
104
+ "height": "高度",
105
+ "frameborder": "框架边框",
106
+ "src_invalid": "输入的URL无效"
107
+ },
98
108
  "image": {
99
109
  "title": "添加/修改图片",
100
110
  "src": "源",
@@ -127,8 +137,9 @@
127
137
  }
128
138
  },
129
139
  "misc": {
130
- "source": "查看源代码",
140
+ "fullscreen": "全屏模式",
131
141
  "preview": "预览",
142
+ "source": "查看源代码",
132
143
  "toggleInvisibleCharacters": "显示/隐藏不可见字符"
133
144
  },
134
145
  "footer": {
@@ -95,6 +95,16 @@
95
95
  "apply": "Použít",
96
96
  "clear": "Vymazat",
97
97
  "close": "Zavřít",
98
+ "iframe": {
99
+ "dialog_title": "Přidat/upravit iframe",
100
+ "src": "Zdroj",
101
+ "name": "Název",
102
+ "title": "Titulek",
103
+ "width": "Šířka",
104
+ "height": "Výška",
105
+ "frameborder": "Okraj rámce",
106
+ "src_invalid": "Zadaná URL adresa je neplatná"
107
+ },
98
108
  "image": {
99
109
  "title": "Přidání/úprava obrázku",
100
110
  "src": "Zdroj",
@@ -127,8 +137,9 @@
127
137
  }
128
138
  },
129
139
  "misc": {
130
- "source": "Zobrazit zdrojový kód",
140
+ "fullscreen": "Režim celé obrazovky",
131
141
  "preview": "Náhled",
142
+ "source": "Zobrazit zdrojový kód",
132
143
  "toggleInvisibleCharacters": "Zobrazit/skrýt neviditelné znaky"
133
144
  },
134
145
  "footer": {
@@ -95,6 +95,16 @@
95
95
  "apply": "Anwenden",
96
96
  "clear": "Löschen",
97
97
  "close": "Schließen",
98
+ "iframe": {
99
+ "dialog_title": "Iframe hinzufügen/bearbeiten",
100
+ "src": "Quelle",
101
+ "name": "Name",
102
+ "title": "Titel",
103
+ "width": "Breite",
104
+ "height": "Höhe",
105
+ "frameborder": "Rahmenrand",
106
+ "src_invalid": "Die eingegebene URL ist ungültig"
107
+ },
98
108
  "image": {
99
109
  "title": "Bild hinzufügen/ändern",
100
110
  "src": "Quelle",
@@ -127,8 +137,9 @@
127
137
  }
128
138
  },
129
139
  "misc": {
130
- "source": "Quellcode anzeigen",
140
+ "fullscreen": "Vollbildmodus",
131
141
  "preview": "Vorschau",
142
+ "source": "Quellcode anzeigen",
132
143
  "toggleInvisibleCharacters": "Unsichtbare Zeichen anzeigen/ausblenden"
133
144
  },
134
145
  "footer": {