glib-web 3.24.2 → 3.24.4

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/action.js CHANGED
@@ -71,7 +71,7 @@ import ActionComponentsSet from "./actions/components/set";
71
71
 
72
72
  import ActionListsAppend from "./actions/lists/append";
73
73
 
74
- import { vueApp } from "./store";
74
+ import { dialogs, vueApp } from "./store";
75
75
 
76
76
  const actions = {
77
77
  runMultiple: ActionsRunMultiple,
@@ -206,7 +206,7 @@ export default class Action {
206
206
 
207
207
  if (response.header || response.body || response.footer) {
208
208
  Utils.http.forceComponentUpdate(() => {
209
- const dialog = component.$closest("dialog");
209
+ const dialog = dialogs.last();
210
210
  const updateDialog = windowMode ? false : Utils.type.isObject(dialog);
211
211
  if (updateDialog) {
212
212
  dialog.updateContent(response);
@@ -1,3 +1,8 @@
1
+ const setFileModel = (component, value) => {
2
+ component.fieldModel = component.$sanitizeValue(component.$externalizeValue(value));
3
+ };
4
+
5
+
1
6
  export default class {
2
7
  execute(spec, component) {
3
8
  let targetComponent = component;
@@ -7,8 +12,9 @@ export default class {
7
12
  }
8
13
 
9
14
  Utils.type.ifObject(spec.data, (data) => {
10
- targetComponent.action_merge(data)
11
- })
15
+ targetComponent.action_merge(data);
16
+ if (data.value) setFileModel(targetComponent, data.value);
17
+ });
12
18
 
13
19
  GLib.action.execute(spec.onSet, targetComponent);
14
20
  }
@@ -5,7 +5,7 @@ export default class {
5
5
  document.activeElement.blur();
6
6
 
7
7
  nextTick(() => {
8
- Action.execute(spec.onBlur, targetComponent);
9
- })
8
+ Action.execute(spec.onBlur, component);
9
+ });
10
10
  }
11
11
  }
@@ -11,7 +11,7 @@ export default class {
11
11
  targetComponent.action_focus();
12
12
 
13
13
  nextTick(() => {
14
- Action.execute(spec.onFocus, targetComponent);
15
- })
14
+ Action.execute(spec.onFocus, component);
15
+ });
16
16
  }
17
17
  }
package/app.vue CHANGED
@@ -35,14 +35,16 @@ import FormPanel from "./components/panels/form.vue";
35
35
  import { vueApp } from "./store";
36
36
  import { useDirtyState } from "./components/composable/dirtyState";
37
37
  import { useSocket } from "./components/composable/socket";
38
- import { uploadFiles, useFilesState, useDropableUtils, setBusyWhenUploading } from "./components/composable/dropable";
39
- import { ref, watch } from "vue";
38
+ import { usePasteable } from "./components/composable/pasteable";
39
+ import { computed } from "vue";
40
40
 
41
41
  const { watchDirtyState } = useDirtyState();
42
- const { signedIds } = useDropableUtils();
43
42
 
44
43
  export default {
45
- setup() {
44
+ setup(props) {
45
+ const filePaster = computed(() => props.page.filePaster);
46
+ usePasteable(filePaster);
47
+
46
48
  return { vueApp };
47
49
  },
48
50
  components: {
@@ -95,7 +97,6 @@ export default {
95
97
  handler(val, oldVal) {
96
98
  if (!val) return;
97
99
  this.handleActionCable(val);
98
- this.handlePasteFile(val);
99
100
  },
100
101
  immediate: true
101
102
  }
@@ -123,41 +124,6 @@ export default {
123
124
  this.actionCableConsumer = null;
124
125
  }
125
126
  },
126
- handlePasteFile(val) {
127
- // paste event
128
- if (val.pasteFile) {
129
- document.addEventListener('paste', this.onPaste);
130
- } else {
131
- document.removeEventListener('paste', this.onPaste);
132
- }
133
- },
134
- async onPaste(event) {
135
- const { directUploadUrl, accepts, url, inputName } = this.page.pasteFile;
136
- const pastedFiles = event.clipboardData.files;
137
- if (pastedFiles.length <= 0) return;
138
-
139
- const files = ref({});
140
- setBusyWhenUploading({ files });
141
- const { uploaded } = useFilesState(files);
142
- watch(uploaded, (val) => {
143
- if (val) {
144
- GLib.action.execute({
145
- action: 'http/post',
146
- url,
147
- formData: inputName ? { [inputName]: { signed_ids: signedIds(files) } } : { signed_ids: signedIds(files) }
148
- }, this);
149
- files.value = {};
150
- }
151
- });
152
- uploadFiles({
153
- droppedFiles: pastedFiles,
154
- files,
155
- accepts,
156
- directUploadUrl,
157
- responseMessages: {}
158
- });
159
-
160
- },
161
127
  $mounted() {
162
128
  window.addEventListener(
163
129
  "resize",
@@ -278,7 +278,7 @@ export default {
278
278
  }
279
279
  },
280
280
  $registryEnabled() {
281
- return false;
281
+ return true;
282
282
  },
283
283
  $jsonLogicEnabled() {
284
284
  return false;
@@ -2,6 +2,7 @@ import { computed, watch, onMounted, nextTick } from 'vue';
2
2
  import { triggerOnChange } from "./form";
3
3
  import Uploader from "../../utils/uploader";
4
4
  import { vueApp } from "../../store";
5
+ import Action from "../../action";
5
6
 
6
7
  function useDropableUtils() {
7
8
  const makeKey = () => Math.random().toString(36).slice(2, 7);
@@ -77,11 +78,31 @@ function setBusyWhenUploading({ files }) {
77
78
  });
78
79
  }
79
80
 
81
+ const showError = (options) => {
82
+ const { body, button } = options;
83
+ Action.execute(
84
+ {
85
+ action: 'snackbars/select',
86
+ message: body,
87
+ styleClasses: ['error', 'vertical'],
88
+ buttons: [{ text: button }]
89
+ },
90
+ {}
91
+ );
92
+ };
80
93
  function uploadFiles({ droppedFiles, files, directUploadUrl, accepts = {}, responseMessages = {}, container }) {
81
- const maxFileLength = accepts.maxFileLength || 10;
94
+ const { maxFileLength } = accepts;
95
+ // const droppedFilesSizeInByte = Array.from(droppedFiles).reduce((prev, curr) => prev + curr.size, 0);
96
+
97
+ // if (droppedFilesSizeInByte > maxFileSize * 1000) {
98
+ // showError(accepts.maxFileSizeErrorText);
99
+
100
+ // return;
101
+ // }
82
102
 
83
103
  if (droppedFiles.length > maxFileLength) {
84
- alert("Files exceed the maximum limit per upload");
104
+ showError(accepts.maxFileLengthErrorText);
105
+
85
106
  return;
86
107
  }
87
108
 
@@ -0,0 +1,49 @@
1
+ import { onMounted, ref, watch } from "vue";
2
+ import { uploadFiles, useFilesState, setBusyWhenUploading, useDropableUtils } from "./dropable";
3
+
4
+ const { signedIds } = useDropableUtils();
5
+
6
+ const onPaste = async (event, filePaster) => {
7
+ const { directUploadUrl, accepts, url, inputName } = filePaster;
8
+ const pastedFiles = event.clipboardData.files;
9
+ if (pastedFiles.length <= 0) return;
10
+
11
+ const files = ref({});
12
+ setBusyWhenUploading({ files });
13
+ const { uploaded } = useFilesState(files);
14
+ watch(uploaded, (val) => {
15
+ if (val) {
16
+ GLib.action.execute({
17
+ action: 'http/post',
18
+ url,
19
+ formData: inputName ? { [inputName]: { signed_ids: signedIds(files) } } : { signed_ids: signedIds(files) }
20
+ }, {});
21
+ files.value = {};
22
+ }
23
+ });
24
+ uploadFiles({
25
+ droppedFiles: pastedFiles,
26
+ files,
27
+ accepts,
28
+ directUploadUrl,
29
+ responseMessages: {}
30
+ });
31
+
32
+ };
33
+
34
+ let handler = () => { };
35
+
36
+ function usePasteable(filePaster) {
37
+ handler = (event) => onPaste(event, filePaster.value);
38
+ onMounted(() => {
39
+ watch(filePaster, (val, oldVal) => {
40
+ if (val) {
41
+ document.addEventListener('paste', handler);
42
+ } else {
43
+ document.removeEventListener('paste', handler);
44
+ }
45
+ }, { immediate: true });
46
+ });
47
+ }
48
+
49
+ export { usePasteable };
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <div ref="container" :style="$styles()" :class="classes()">
3
- <v-autocomplete :color="gcolor" v-model="fieldModel" :label="label" :items="options || []" :chips="spec.multiple"
4
- :multiple="spec.multiple" :readonly="spec.readOnly" :clearable="!spec.readOnly" :placeholder="spec.placeholder"
5
- :rules="rules" persistent-hint :append-icon="append.icon" validate-on="blur" item-title='text' :variant="variant"
6
- :closable-chips="spec.multiple" :density="density" persistent-placeholder @update:modelValue="onChange"
7
- @focus="focused = true" @blur="focused = false">
3
+ <v-autocomplete :color="gcolor" v-model="fieldModel" :label="label" :items="normalizedOptions"
4
+ :chips="spec.multiple" :multiple="spec.multiple" :readonly="spec.readOnly" :clearable="!spec.readOnly"
5
+ :placeholder="spec.placeholder" :rules="rules" persistent-hint :append-icon="append.icon" validate-on="blur"
6
+ item-title='text' :variant="variant" :closable-chips="spec.multiple" :density="density" persistent-placeholder
7
+ @update:modelValue="onChange" @focus="focused = true" @blur="focused = false">
8
8
 
9
9
  <template #item="{ props, item }">
10
10
  <v-list-subheader v-if="item.raw.header">
@@ -54,6 +54,20 @@ export default {
54
54
  };
55
55
  },
56
56
  computed: {
57
+ normalizedOptions() {
58
+ return this.spec.options.map(i => {
59
+ switch (i.type) {
60
+ case "label":
61
+ return { header: i.text };
62
+ case "divider":
63
+ return { divider: true };
64
+ default:
65
+ return Object.assign({}, i, {
66
+ props: { subtitle: i.subtitle }
67
+ });
68
+ }
69
+ });
70
+ },
57
71
  values() {
58
72
  // Depends on whether the field is single or multiple
59
73
  if (this.$type.isArray(this.fieldModel)) {
@@ -88,25 +102,11 @@ export default {
88
102
  $ready() {
89
103
  this.updateData(false);
90
104
  },
91
- normalizedOptions() {
92
- return this.spec.options.map(i => {
93
- switch (i.type) {
94
- case "label":
95
- return { header: i.text };
96
- case "divider":
97
- return { divider: true };
98
- default:
99
- return Object.assign({}, i, {
100
- props: { subtitle: i.subtitle }
101
- });
102
- }
103
- });
104
- },
105
105
  classes() {
106
106
  return this.$classes().concat("g-text-field--hintless");
107
107
  },
108
108
  updateData(reinitValue) {
109
- this.options = this.normalizedOptions();
109
+ // this.options = this.normalizedOptions();
110
110
  this.append = this.spec.append || {};
111
111
  this.rules = this.$validation();
112
112
 
@@ -22,6 +22,11 @@ export default {
22
22
  uncheckValue: this.spec.uncheckValue
23
23
  };
24
24
  },
25
+ computed: {
26
+ fieldName() {
27
+ return this.spec.name || this.spec.parentName;
28
+ }
29
+ },
25
30
  methods: {
26
31
  $ready() {
27
32
  this.updateData();
@@ -45,14 +50,6 @@ export default {
45
50
  updateData() {
46
51
  this.groupElement = this.$el.closest("[data-component=checkGroup]");
47
52
 
48
- let groupName = null;
49
- this.$type.ifObject(
50
- this.groupElement,
51
- val => (groupName = val.getAttribute("name"))
52
- );
53
-
54
- this.fieldName = this.spec.name || groupName;
55
-
56
53
  // this.fieldModel = this.spec.checked
57
54
  // ? this.spec.checkValue
58
55
  // : this.spec.value;
@@ -70,12 +70,13 @@ export default {
70
70
  childSpec(item) {
71
71
  if (this.spec.readOnly) {
72
72
  return Object.assign(item, {
73
+ parentName: this.spec.name,
73
74
  readOnly: this.spec.readOnly,
74
75
  onChange: this.spec.onChange,
75
76
  });
76
77
  }
77
78
 
78
- return Object.assign(item, { onChange: this.spec.onChange });
79
+ return Object.assign(item, { onChange: this.spec.onChange, parentName: this.spec.name });
79
80
  },
80
81
  },
81
82
  };
@@ -11,7 +11,6 @@ import { triggerOnChange } from "../composable/form";
11
11
  import inputVariant from "../mixins/inputVariant";
12
12
 
13
13
  export default {
14
- expose: ['fieldModel'],
15
14
  mixins: [inputVariant],
16
15
  props: {
17
16
  spec: { type: Object, required: true },
@@ -1,8 +1,5 @@
1
1
  <template>
2
- <fields-genericSelect
3
- ref="delegate"
4
- :spec="updatedSpec"
5
- />
2
+ <fields-genericSelect ref="delegate" :spec="updatedSpec" />
6
3
  </template>
7
4
 
8
5
  <script>
@@ -16,7 +16,6 @@ import inputVariant from "../mixins/inputVariant";
16
16
  import { determineDensity } from "../../utils/constant";
17
17
 
18
18
  export default {
19
- expose: ['fieldModel'],
20
19
  mixins: [inputVariant],
21
20
  props: {
22
21
  spec: { type: Object, required: true },
@@ -13,22 +13,7 @@ export default {
13
13
  props: {
14
14
  spec: { type: Object, required: true }
15
15
  },
16
- data: function () {
17
- return {
18
- // text: " "
19
- };
20
- },
21
16
  methods: {
22
- $ready() {
23
- // this.$wsInitActionCable(this.spec.actionCable);
24
-
25
- // this.text = this.spec.text;
26
- },
27
- // action_set(spec) {
28
- // if (spec.user_id !== spec.filterKey) {
29
- // this.text = spec.text;
30
- // }
31
- // },
32
17
  action_merge(mergedSpec) {
33
18
  Object.assign(this.spec, mergedSpec);
34
19
  this.$ready();
@@ -30,12 +30,12 @@ export default {
30
30
  },
31
31
  created() {
32
32
  // watch components/update
33
- if (this.spec) {
34
- this.$watch(
35
- () => Object.keys(this.spec).map((specKey) => this.spec[specKey]),
36
- () => this._ready()
37
- );
38
- }
33
+ // if (this.spec) {
34
+ // this.$watch(
35
+ // () => Object.keys(this.spec).map((specKey) => this.spec[specKey]),
36
+ // () => this._ready()
37
+ // );
38
+ // }
39
39
 
40
40
  this.$created();
41
41
  },
@@ -32,7 +32,7 @@ export default {
32
32
  Utils.type.ifObject(val.format, spec => {
33
33
  augmentedRules = augmentedRules.concat([
34
34
  v => {
35
- if (v && !v.match(spec.regex)) {
35
+ if (v && !v.toString().match(spec.regex)) {
36
36
  return spec.message;
37
37
  }
38
38
  return true;
@@ -11,7 +11,7 @@ const isNeedToBeFixed = (val, component) => {
11
11
  export default {
12
12
  data: function () {
13
13
  return {
14
- fieldName: null,
14
+ // fieldName: null,
15
15
  fieldModel: null,
16
16
  _show: true,
17
17
  _watchers: [],
@@ -35,6 +35,9 @@ export default {
35
35
  panelContext: { default: {} }
36
36
  },
37
37
  computed: {
38
+ fieldName() {
39
+ if (this.spec && this.spec.name) return this.spec.name;
40
+ },
38
41
  inputDisabled() {
39
42
  let disabled = false;
40
43
  if (Object.keys(this.panelContext).length > 0) { // handle nested
@@ -82,7 +85,7 @@ export default {
82
85
  },
83
86
  methods: {
84
87
  viewKey(item, index) {
85
- if (!item) return '';
88
+ if (!item || !item.view) return '';
86
89
  // random string to trigger render
87
90
  const forceUpdate = (Math.random() + 1).toString(36).substring(7);
88
91
  if (item.view.startsWith('charts/')) return forceUpdate;
@@ -266,7 +269,7 @@ export default {
266
269
  if (hasCondition || isField) {
267
270
  // Has to be executed before $ready(). This executes regardless of whether a form is found because fields
268
271
  // may be used without a form.
269
- this.fieldName = this.spec.name;
272
+ // this.fieldName = this.spec.name;
270
273
 
271
274
  // Avoid user input getting removed when updating certain parts of the page.
272
275
  if (valueChanged) {
package/components/p.vue CHANGED
@@ -1,6 +1,5 @@
1
1
  <template>
2
- <!-- <p :style="genericStyles()" :class="$classes()" v-html="compiledText" /> -->
3
- <p :style="genericStyles()" :class="$classes()">{{ text }}</p>
2
+ <p :style="$styles()" :class="$classes()">{{ spec.text }}</p>
4
3
  </template>
5
4
 
6
5
  <script>
@@ -9,21 +8,7 @@
9
8
  export default {
10
9
  props: {
11
10
  spec: { type: Object, required: true }
12
- },
13
- computed: {
14
- text() {
15
- return this.spec.text;
16
- }
17
11
  }
18
- // computed: {
19
- // compiledText() {
20
- // if (this.spec.format == "markdown") {
21
- // return marked(this.spec.text, { sanitize: true });
22
- // } else {
23
- // return this.spec.text;
24
- // }
25
- // }
26
- // }
27
12
  };
28
13
  </script>
29
14
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-col :xxl="xxl.cols" :xl="xl.cols" :lg="lg.cols" :md="md.cols" :sm="sm.cols" :style="cssStyles()" :class="$classes()"
2
+ <v-col :xxl="xxl.cols" :xl="xl.cols" :lg="lg.cols" :md="md.cols" :sm="sm.cols" :style="cssStyles" :class="$classes()"
3
3
  :cols="xs.cols || 12" :order-xl="xl.order" :order-lg="lg.order" :order-md="md.order" :order-sm="sm.order"
4
4
  :order="xs.order">
5
5
  <common-responsive :spec="innerSpec()" :full-height="fullHeight" />
@@ -26,21 +26,7 @@ export default {
26
26
  computed: {
27
27
  fullHeight() {
28
28
  // Provide a default name so the panel will not be nameless when used in predefined layouts (e.g. page.body, list.header, etc.)
29
- return this.spec.height == 'matchParent'
30
- },
31
- },
32
- methods: {
33
- $ready() {
34
- this.xxl = this.spec.xxl || this.xxl;
35
- this.xl = this.spec.xl || this.xl;
36
- this.lg = this.spec.lg || this.lg;
37
- this.md = this.spec.md || this.md;
38
- this.sm = this.spec.sm || this.sm;
39
- this.xs = this.spec.xs || this.xs;
40
- },
41
- innerSpec() {
42
- // Remove properties that are handled by the container (i.e. v-col).
43
- return new Hash(this.spec).without("padding").without("styleClasses");
29
+ return this.spec.height == 'matchParent';
44
30
  },
45
31
  cssStyles() {
46
32
  const styles = Object.assign({}, this.$styles());
@@ -111,7 +97,22 @@ export default {
111
97
  // Don't interfere with v-col's height (which is full height by default anyway) in order not to break the full height behaviour.
112
98
  // Let this be handled by the child (responsive) panel instead.
113
99
  return new Hash(styles).without("height");
100
+ }
101
+ },
102
+ methods: {
103
+ $ready() {
104
+ this.xxl = this.spec.xxl || this.xxl;
105
+ this.xl = this.spec.xl || this.xl;
106
+ this.lg = this.spec.lg || this.lg;
107
+ this.md = this.spec.md || this.md;
108
+ this.sm = this.spec.sm || this.sm;
109
+ this.xs = this.spec.xs || this.xs;
114
110
  },
111
+ innerSpec() {
112
+ // Remove properties that are handled by the container (i.e. v-col).
113
+ return new Hash(this.spec).without("padding").without("styleClasses");
114
+ },
115
+
115
116
  applyStyles(styles, spec) {
116
117
  Object.assign(styles, this.$styles(spec));
117
118
  Utils.type.ifBoolean(spec.hide, () => {
@@ -196,6 +196,7 @@ export default defineComponent({
196
196
  }
197
197
  };
198
198
  const handleDragOver = (event, item) => {
199
+ if (item.children) item._open = true;
199
200
  if (item.dropData) {
200
201
  event.preventDefault();
201
202
  lastDragItem.value = item;
package/nav/dialog.vue CHANGED
@@ -45,12 +45,17 @@ import Action from "../action";
45
45
  import FormPanel from "../components/panels/form.vue";
46
46
  import { useDirtyState } from "../components/composable/dirtyState";
47
47
  import { dialogs } from "../store";
48
+ import { ref } from "vue";
49
+ import { usePasteable } from "../components/composable/pasteable";
48
50
 
49
51
  export default {
50
52
  expose: ['isFormDirty', 'model'],
51
- setup() {
53
+ setup(props) {
54
+ const filePaster = ref(undefined);
55
+ usePasteable(filePaster);
56
+
52
57
  const { isDirty } = useDirtyState();
53
- return { isDirty };
58
+ return { isDirty, filePaster };
54
59
  },
55
60
  components: {
56
61
  "panels-form": FormPanel,
@@ -129,6 +134,7 @@ export default {
129
134
  // },
130
135
  close() {
131
136
  if (!this.isDirty()) {
137
+ this.filePaster = null;
132
138
  dialogs.remove(this);
133
139
  this.model = false;
134
140
  }
@@ -167,6 +173,7 @@ export default {
167
173
  "GET",
168
174
  this,
169
175
  (response) => {
176
+ this.filePaster = response.filePaster;
170
177
  Utils.http.forceComponentUpdate(() => {
171
178
  this.urlLoaded = true;
172
179
  this.message = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "3.24.2",
3
+ "version": "3.24.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -19,7 +19,8 @@ export default {
19
19
  GLib.component.vueName(this)
20
20
  );
21
21
  }
22
- GLib.component.register(id, this);
22
+ const newComponent = this.$refs.delegate || this;
23
+ GLib.component.register(id, newComponent);
23
24
  }
24
25
  },
25
26
  $tearDown() {
@@ -30,7 +31,7 @@ export default {
30
31
  },
31
32
  $registryEnabled() {
32
33
  // Common classes such as `_select` need to return false so that it doesn't override its parent (e.g. `select`).
33
- return true;
34
+ return false;
34
35
  }
35
36
  }
36
37
  });
package/utils/http.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import Type from "./type";
2
2
  import Action from "../action";
3
3
  import { nextTick } from 'vue';
4
- import { ctx, vueApp } from "../store";
4
+ import { ctx, dialogs, vueApp } from "../store";
5
5
 
6
6
  let loading = false;
7
7
 
@@ -80,7 +80,7 @@ export default class {
80
80
  const htmlUrl = Utils.url.htmlUrl(properties["url"]);
81
81
 
82
82
  Utils.http.execute(properties, "GET", component, (data, response) => {
83
- const pushHistory = windowMode ? true : !Utils.type.isObject(component.$closest("dialog"));
83
+ const pushHistory = windowMode ? true : !Utils.type.isObject(dialogs.last());
84
84
  if (htmlUrl !== currentUrl && pushHistory) {
85
85
  const redirectUrl = Utils.url.htmlUrl(response.url);
86
86
  Utils.history.pushPage(data, redirectUrl);
package/utils/uploader.js CHANGED
@@ -65,7 +65,7 @@ export default class Uploader {
65
65
  [spec.fileType].flat().forEach(key => {
66
66
  const acceptsRegex = new RegExp(mimeType[key]);
67
67
  if (!mimeType[key]) {
68
- console.error(`Mime type not found: ${key}`)
68
+ console.error(`Mime type not found: ${key}`);
69
69
  }
70
70
  if (acceptsRegex.test(this.file.type)) {
71
71
  validFileType = true;
@@ -79,7 +79,8 @@ export default class Uploader {
79
79
 
80
80
  if (spec.maxFileSize) {
81
81
  if (this.file.size > spec.maxFileSize * MB_SIZE) {
82
- alert(spec.maxFileSizeErrorText || "File too big");
82
+ // support for old version, maxFileSizeErrorText is type of Object not String
83
+ if (typeof spec.maxFileSizeErrorText == 'string') alert(spec.maxFileSizeErrorText || "File too big");
83
84
  return false;
84
85
  }
85
86
  }