vue-laravel-crud 2.0.4 → 2.0.6

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,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-laravel-crud",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "homepage": "https://github.com/clonixdev/vue-laravel-crud",
@@ -53,6 +53,7 @@
53
53
  "cors": "^2.8.5",
54
54
  "cross-env": "^7.0.3",
55
55
  "express": "^4.18.2",
56
+ "marked": "^4.3.0",
56
57
  "minimist": "^1.2.5",
57
58
  "node-sass": "^8.0.0",
58
59
  "rollup": "^2.68.0",
package/src/ItemCard.vue CHANGED
@@ -15,11 +15,29 @@
15
15
  {{ itemValue(column, item) }}
16
16
  </span>
17
17
  <span v-else-if="column.type === 'state'">
18
- {{ getStateValue(itemValue(column, item), column.options) }}
18
+ <template v-if="getStateOptionsForColumn(column, item).length > 0">
19
+ <b-badge
20
+ v-for="(option, optIndex) in getStateOptionsForColumn(column, item)"
21
+ :key="optIndex"
22
+ :variant="getStateBadgeVariant(option)"
23
+ class="mr-1"
24
+ >
25
+ {{ option.text }}
26
+ </b-badge>
27
+ </template>
28
+ <span v-else>
29
+ {{ itemValue(column, item) }}
30
+ </span>
19
31
  </span>
20
32
  <span v-else-if="column.type === 'array'">
21
33
  {{ getArrayValue(itemValue(column, item), column.displayProp, column.options) }}
22
34
  </span>
35
+ <span v-else-if="column.type === 'money' || column.type === 'price'">
36
+ {{ formatMoney(itemValue(column, item), column) }}
37
+ </span>
38
+ <span v-else-if="column.type === 'number' && (column.thousandsSeparator || column.decimalSeparator || column.decimals !== undefined)">
39
+ {{ formatNumber(itemValue(column, item), column) }}
40
+ </span>
23
41
  <span v-else>
24
42
  {{ itemValue(column, item) }}
25
43
  </span>
@@ -29,11 +47,14 @@
29
47
  </slot>
30
48
  <template v-slot:footer>
31
49
  <b-button-group>
32
- <slot name="rowAction" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
50
+ <slot name="rowActions" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
33
51
  v-bind:updateItem="updateItem" v-bind:removeItem="removeItem">
34
- <b-button variant="primary" @click="showItem(item.id, index)"><b-icon-eye></b-icon-eye></b-button>
35
- <b-button variant="secondary" @click="updateItem(item.id, index)"><b-icon-pencil></b-icon-pencil></b-button>
36
- <b-button variant="danger" @click="removeItem(item.id, index)"><b-icon-trash></b-icon-trash></b-button>
52
+ <slot name="rowAction" v-bind:item="item" v-bind:index="index" v-bind:showItem="showItem"
53
+ v-bind:updateItem="updateItem" v-bind:removeItem="removeItem">
54
+ <b-button variant="primary" @click="showItem(item.id, index)"><b-icon-eye></b-icon-eye></b-button>
55
+ <b-button variant="secondary" @click="updateItem(item.id, index)"><b-icon-pencil></b-icon-pencil></b-button>
56
+ <b-button variant="danger" @click="removeItem(item.id, index)"><b-icon-trash></b-icon-trash></b-button>
57
+ </slot>
37
58
  </slot>
38
59
  </b-button-group>
39
60
  </template>
@@ -55,11 +76,53 @@
55
76
  cardHideFooter: Boolean,
56
77
  itemValue: Function,
57
78
  getStateValue: Function,
79
+ getStateOptions: Function,
80
+ getStateBadgeVariant: Function,
58
81
  getArrayValue: Function,
59
82
  showItem: Function,
60
83
  updateItem: Function,
61
84
  removeItem: Function,
62
85
  },
86
+ methods: {
87
+ getStateOptionsForColumn(column, item) {
88
+ if (column.type === 'state' && column.options) {
89
+ return this.getStateOptions(this.itemValue(column, item), column.options);
90
+ }
91
+ return [];
92
+ },
93
+ formatNumber(value, column) {
94
+ if (value === null || value === undefined || value === '') {
95
+ return '';
96
+ }
97
+
98
+ const numValue = parseFloat(value);
99
+ if (isNaN(numValue)) {
100
+ return value;
101
+ }
102
+
103
+ const thousandsSep = column.thousandsSeparator || '.';
104
+ const decimalSep = column.decimalSeparator || ',';
105
+ const decimals = column.decimals !== undefined ? column.decimals : (numValue % 1 === 0 ? 0 : 2);
106
+
107
+ // Formatear número con separadores
108
+ const parts = numValue.toFixed(decimals).split('.');
109
+ const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep);
110
+ const decimalPart = parts[1] || '';
111
+
112
+ if (decimals > 0 && decimalPart) {
113
+ return `${integerPart}${decimalSep}${decimalPart}`;
114
+ }
115
+ return integerPart;
116
+ },
117
+ formatMoney(value, column) {
118
+ const formatted = this.formatNumber(value, column);
119
+ if (formatted === '') {
120
+ return '';
121
+ }
122
+ const symbol = column.symbol || '$';
123
+ return `${symbol}${formatted}`;
124
+ }
125
+ }
63
126
  };
64
127
  </script>
65
128
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-if="displayMode == displayModes.MODE_CARDS">
2
+ <div v-if="currentDisplayMode == displayModes.MODE_CARDS">
3
3
  <draggable
4
4
  v-model="items"
5
5
  :group="draggableGroup"
@@ -24,12 +24,18 @@
24
24
  :cardClass="cardClass"
25
25
  :cardHideFooter="cardHideFooter"
26
26
  :itemValue="itemValue"
27
- :getStateValue="getStateValue"
27
+ :getStateValue="getStateValue"
28
+ :getStateOptions="getStateOptions"
29
+ :getStateBadgeVariant="getStateBadgeVariant"
28
30
  :getArrayValue="getArrayValue"
29
31
  :showItem="showItem"
30
32
  :updateItem="updateItem"
31
- :removeItem="removeItem"
32
- />
33
+ :removeItem="removeItem"
34
+ >
35
+ <template v-for="(slot, name) in $scopedSlots" v-slot:[name]="slotProps">
36
+ <slot :name="name" v-bind="slotProps" />
37
+ </template>
38
+ </ItemCard>
33
39
  </slot>
34
40
  </div>
35
41
  </masonry>
@@ -74,6 +80,8 @@ export default {
74
80
  'cardHideFooter',
75
81
  'itemValue',
76
82
  'getStateValue',
83
+ 'getStateOptions',
84
+ 'getStateBadgeVariant',
77
85
  'getArrayValue',
78
86
  'showItem',
79
87
  'updateItem',
@@ -89,6 +97,18 @@ export default {
89
97
  return {
90
98
  drag: false
91
99
  };
100
+ },
101
+ computed: {
102
+ currentDisplayMode() {
103
+ if (!this.displayMode) return 1;
104
+ if (this.displayMode.value !== undefined) {
105
+ return this.displayMode.value;
106
+ }
107
+ if (typeof this.displayMode === 'function') {
108
+ return this.displayMode();
109
+ }
110
+ return this.displayMode;
111
+ }
92
112
  }
93
113
  };
94
114
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-if="displayMode == displayModes.MODE_CUSTOM">
2
+ <div v-if="currentDisplayMode == displayModes.MODE_CUSTOM">
3
3
  <div :class="listContainerClass">
4
4
  <p v-if="!loading && itemsList && itemsList.length == 0 && !infiniteScroll" class="p-3">
5
5
  {{ messageEmptyResults }}
@@ -24,6 +24,18 @@ export default {
24
24
  'infiniteScroll',
25
25
  'messageEmptyResults',
26
26
  'itemsList'
27
- ]
27
+ ],
28
+ computed: {
29
+ currentDisplayMode() {
30
+ if (!this.displayMode) return 1;
31
+ if (this.displayMode.value !== undefined) {
32
+ return this.displayMode.value;
33
+ }
34
+ if (typeof this.displayMode === 'function') {
35
+ return this.displayMode();
36
+ }
37
+ return this.displayMode;
38
+ }
39
+ }
28
40
  };
29
41
  </script>
@@ -3,11 +3,11 @@
3
3
  <div v-for="(column, indexc) in columns" :key="indexc">
4
4
  <div v-if="isColumnHasFilter(column)">
5
5
  <slot :name="'sidebar-filter-' + column.prop" v-bind:column="column" v-bind:filter="filter"
6
- v-bind:internalFilterByProp="internalFilterByProp" v-if="internalFilterByProp(column.prop)">
6
+ v-bind:internalFilterByProp="internalFilterByProp" v-bind:getFilterForColumn="getFilterForColumn">
7
7
  <div class="form-group" v-if="column.type == 'boolean'">
8
8
  <label>{{ column.label }}</label>
9
9
 
10
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
10
+ <select class="form-control" v-model="getFilterForColumn(column).value"
11
11
  @change="onChangeFilter($event)">
12
12
  <option value=""></option>
13
13
  <option value="1">Sí</option>
@@ -17,31 +17,49 @@
17
17
  <div class="form-group" v-else-if="column.type == 'date'">
18
18
  <div class="row">
19
19
  <div class="col-6">
20
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_from').value
20
+ <b-form-datepicker v-model="getFilterForDateFrom(column).value
21
21
  " today-button reset-button close-button locale="es"></b-form-datepicker>
22
22
  </div>
23
23
  <div class="col-6">
24
- <b-form-datepicker v-model="internalFilterByProp(column.prop + '_to').value
24
+ <b-form-datepicker v-model="getFilterForDateTo(column).value
25
25
  " today-button reset-button close-button locale="es"></b-form-datepicker>
26
26
  </div>
27
27
  </div>
28
28
  </div>
29
29
 
30
+ <div class="form-group" v-else-if="column.type == 'number' || column.type == 'money'">
31
+ <label>{{ column.label }}</label>
32
+ <div class="row">
33
+ <div class="col-6">
34
+ <input
35
+ type="number"
36
+ class="form-control"
37
+ v-model.number="getFilterForDateFrom(column).value"
38
+ :step="column.type == 'money' ? '0.01' : '1'"
39
+ @change="onChangeFilter($event)"
40
+ placeholder="Desde" />
41
+ </div>
42
+ <div class="col-6">
43
+ <input
44
+ type="number"
45
+ class="form-control"
46
+ v-model.number="getFilterForDateTo(column).value"
47
+ :step="column.type == 'money' ? '0.01' : '1'"
48
+ @change="onChangeFilter($event)"
49
+ placeholder="Hasta" />
50
+ </div>
51
+ </div>
52
+ </div>
53
+
30
54
  <div class="form-group" v-else-if="column.type == 'state'">
31
55
  <label>{{ column.label }}</label>
32
56
 
33
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
34
- @change="onChangeFilter($event)" v-if="optionsLoaded">
57
+ <select class="form-control" v-model="getFilterForColumn(column).value"
58
+ @change="onChangeFilter($event)" v-if="column.options && Array.isArray(column.options)">
35
59
  <option value=""></option>
36
- <option :value="option.id ? option.id : option.value" v-for="option in column.options"
37
- :key="option.id ? option.id : option.value">
38
- {{
39
- option.text
40
- ? option.text
41
- : option.label
42
- ? option.label
43
- : ""
44
- }}
60
+ <option :value="option.value" v-for="option in column.options"
61
+ :key="option.value || option.id">
62
+ {{ option.text }}
45
63
  </option>
46
64
  </select>
47
65
  </div>
@@ -49,34 +67,132 @@
49
67
  <div class="form-group" v-else-if="column.type == 'array'">
50
68
  <label>{{ column.label }}</label>
51
69
 
52
- <select class="form-control" v-model="internalFilterByProp(column.prop).value"
53
- @change="onChangeFilter($event)" v-if="optionsLoaded">
70
+ <select class="form-control" v-model="getFilterForColumn(column).value"
71
+ @change="onChangeFilter($event)" v-if="column.options && Array.isArray(column.options)">
54
72
  <option value=""></option>
55
- <template v-if="column.options">
56
- <option :value="option.id ? option.id : option.value" v-for="option in column.options"
57
- :key="option.id ? option.id : option.value">
58
- {{
59
- option.text
60
- ? option.text
61
- : option.label
62
- ? option.label
63
- : ""
64
- }}
65
- </option>
66
- </template>
73
+ <option :value="option.value" v-for="option in column.options"
74
+ :key="option.value || option.id">
75
+ {{ option.text }}
76
+ </option>
67
77
  </select>
68
78
  </div>
69
79
 
70
80
  <div class="form-group" v-else>
71
81
  <label>{{ column.label }}</label>
72
82
 
73
- <input class="form-control" v-model.lazy="internalFilterByProp(column.prop).value"
83
+ <input class="form-control" v-model.lazy="getFilterForColumn(column).value"
74
84
  @change="onChangeFilter($event)" />
75
85
  </div>
76
86
  </slot>
77
87
  </div>
78
88
  </div>
79
89
 
90
+ <!-- Filtros custom -->
91
+ <div v-for="(customFilter, indexcf) in customFilters" :key="'custom-' + indexcf">
92
+ <div v-if="isCustomFilterEnabled(customFilter)">
93
+ <!-- Slot personalizado para filtro custom -->
94
+ <slot :name="'sidebar-filter-custom-' + customFilter.prop" v-bind:column="customFilter" v-bind:filter="filter"
95
+ v-bind:internalFilterByProp="internalFilterByProp" v-bind:getFilterForColumn="getFilterForColumn">
96
+
97
+ <!-- Si type es una función callback -->
98
+ <RenderCustomFilter
99
+ v-if="typeof customFilter.type === 'function'"
100
+ :render-function="customFilter.type"
101
+ :custom-filter="customFilter"
102
+ :filter="filter"
103
+ :internal-filter-by-prop="internalFilterByProp"
104
+ :get-filter-for-column="getFilterForColumn"
105
+ :on-change-filter="onChangeFilter"
106
+ />
107
+
108
+ <!-- Si type es string, usar la misma lógica que las columnas -->
109
+ <template v-else>
110
+ <div class="form-group" v-if="customFilter.type == 'boolean'">
111
+ <label>{{ customFilter.label }}</label>
112
+
113
+ <select class="form-control" v-model="getFilterForColumn(customFilter).value"
114
+ @change="onChangeFilter($event)">
115
+ <option value=""></option>
116
+ <option value="1">Sí</option>
117
+ <option value="0">No</option>
118
+ </select>
119
+ </div>
120
+
121
+ <div class="form-group" v-else-if="customFilter.type == 'date'">
122
+ <label>{{ customFilter.label }}</label>
123
+ <div class="row">
124
+ <div class="col-6">
125
+ <b-form-datepicker v-model="getFilterForDateFrom(customFilter).value
126
+ " today-button reset-button close-button locale="es"></b-form-datepicker>
127
+ </div>
128
+ <div class="col-6">
129
+ <b-form-datepicker v-model="getFilterForDateTo(customFilter).value
130
+ " today-button reset-button close-button locale="es"></b-form-datepicker>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <div class="form-group" v-else-if="customFilter.type == 'number' || customFilter.type == 'money'">
136
+ <label>{{ customFilter.label }}</label>
137
+ <div class="row">
138
+ <div class="col-6">
139
+ <input
140
+ type="number"
141
+ class="form-control"
142
+ v-model.number="getFilterForDateFrom(customFilter).value"
143
+ :step="customFilter.type == 'money' ? '0.01' : '1'"
144
+ @change="onChangeFilter($event)"
145
+ placeholder="Desde" />
146
+ </div>
147
+ <div class="col-6">
148
+ <input
149
+ type="number"
150
+ class="form-control"
151
+ v-model.number="getFilterForDateTo(customFilter).value"
152
+ :step="customFilter.type == 'money' ? '0.01' : '1'"
153
+ @change="onChangeFilter($event)"
154
+ placeholder="Hasta" />
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <div class="form-group" v-else-if="customFilter.type == 'state'">
160
+ <label>{{ customFilter.label }}</label>
161
+
162
+ <select class="form-control" v-model="getFilterForColumn(customFilter).value"
163
+ @change="onChangeFilter($event)" v-if="customFilter.options && Array.isArray(customFilter.options)">
164
+ <option value=""></option>
165
+ <option :value="option.value" v-for="option in customFilter.options"
166
+ :key="option.value || option.id">
167
+ {{ option.text }}
168
+ </option>
169
+ </select>
170
+ </div>
171
+
172
+ <div class="form-group" v-else-if="customFilter.type == 'array'">
173
+ <label>{{ customFilter.label }}</label>
174
+
175
+ <select class="form-control" v-model="getFilterForColumn(customFilter).value"
176
+ @change="onChangeFilter($event)" v-if="customFilter.options && Array.isArray(customFilter.options)">
177
+ <option value=""></option>
178
+ <option :value="option.value" v-for="option in customFilter.options"
179
+ :key="option.value || option.id">
180
+ {{ option.text }}
181
+ </option>
182
+ </select>
183
+ </div>
184
+
185
+ <div class="form-group" v-else>
186
+ <label>{{ customFilter.label }}</label>
187
+
188
+ <input class="form-control" v-model.lazy="getFilterForColumn(customFilter).value"
189
+ @change="onChangeFilter($event)" />
190
+ </div>
191
+ </template>
192
+ </slot>
193
+ </div>
194
+ </div>
195
+
80
196
  <div class="mt-3 d-flex justify-content-center">
81
197
  <button class="btn btn-light" @click="resetFilters()">
82
198
  Reset
@@ -89,16 +205,115 @@
89
205
  </template>
90
206
 
91
207
  <script>
208
+ // Componente funcional para renderizar filtros custom con callback
209
+ const RenderCustomFilter = {
210
+ functional: true,
211
+ props: {
212
+ renderFunction: {
213
+ type: Function,
214
+ required: true
215
+ },
216
+ customFilter: {
217
+ type: Object,
218
+ required: true
219
+ },
220
+ filter: {
221
+ type: Array,
222
+ default: () => []
223
+ },
224
+ internalFilterByProp: {
225
+ type: Function,
226
+ required: true
227
+ },
228
+ getFilterForColumn: {
229
+ type: Function,
230
+ required: true
231
+ },
232
+ onChangeFilter: {
233
+ type: Function,
234
+ required: true
235
+ }
236
+ },
237
+ render(h, context) {
238
+ const { renderFunction, customFilter, filter, internalFilterByProp, getFilterForColumn, onChangeFilter } = context.props;
239
+ return renderFunction(h, {
240
+ column: customFilter,
241
+ filter: filter,
242
+ internalFilterByProp: internalFilterByProp,
243
+ getFilterForColumn: getFilterForColumn,
244
+ onChangeFilter: onChangeFilter
245
+ });
246
+ }
247
+ };
248
+
92
249
  export default {
93
250
  name: 'CrudFilters',
251
+ components: {
252
+ RenderCustomFilter
253
+ },
94
254
  inject: [
95
255
  'columns',
256
+ 'customFilters',
96
257
  'isColumnHasFilter',
258
+ 'isCustomFilterEnabled',
97
259
  'filter',
98
260
  'internalFilterByProp',
99
261
  'optionsLoaded',
100
262
  'onChangeFilter',
101
- 'resetFilters'
102
- ]
263
+ 'resetFilters',
264
+ 'setupFilters',
265
+ 'internalFilters'
266
+ ],
267
+ methods: {
268
+ // Método helper para obtener el filtro de forma segura, creándolo si no existe
269
+ getFilterForColumn(column) {
270
+ let filter = this.internalFilterByProp(column.prop);
271
+
272
+ // Si el filtro no existe, intentar inicializar los filtros
273
+ if (!filter) {
274
+ // Verificar si hay filtros inicializados
275
+ if (this.internalFilters && this.internalFilters.length === 0) {
276
+ this.setupFilters();
277
+ // Intentar obtener el filtro nuevamente después de inicializar
278
+ filter = this.internalFilterByProp(column.prop);
279
+ }
280
+ }
281
+
282
+ // Si aún no existe, crear un objeto temporal para evitar errores
283
+ if (!filter) {
284
+ return {
285
+ value: null
286
+ };
287
+ }
288
+
289
+ return filter;
290
+ },
291
+
292
+ // Método helper específico para campos de fecha (from)
293
+ getFilterForDateFrom(column) {
294
+ const filter = this.internalFilterByProp(column.prop + '_from');
295
+ if (!filter) {
296
+ if (this.internalFilters && this.internalFilters.length === 0) {
297
+ this.setupFilters();
298
+ return this.internalFilterByProp(column.prop + '_from') || { value: null };
299
+ }
300
+ return { value: null };
301
+ }
302
+ return filter;
303
+ },
304
+
305
+ // Método helper específico para campos de fecha (to)
306
+ getFilterForDateTo(column) {
307
+ const filter = this.internalFilterByProp(column.prop + '_to');
308
+ if (!filter) {
309
+ if (this.internalFilters && this.internalFilters.length === 0) {
310
+ this.setupFilters();
311
+ return this.internalFilterByProp(column.prop + '_to') || { value: null };
312
+ }
313
+ return { value: null };
314
+ }
315
+ return filter;
316
+ }
317
+ }
103
318
  };
104
319
  </script>
@@ -30,8 +30,8 @@
30
30
  <b-button variant="info" v-if="enableFilters" @click="toggleFilters()">Filtros</b-button>
31
31
  <b-button variant="info" @click="refresh()"><b-icon-arrow-clockwise></b-icon-arrow-clockwise></b-button>
32
32
  <b-button variant="info" @click="toggleDisplayMode()" :disabled="loading" v-if="displayModeToggler">
33
- <b-icon-card-list v-if="displayMode == displayModes.MODE_TABLE"></b-icon-card-list>
34
- <b-icon-table v-else-if="displayMode == displayModes.MODE_CARDS"></b-icon-table>
33
+ <b-icon-card-list v-if="currentDisplayMode == displayModes.MODE_TABLE"></b-icon-card-list>
34
+ <b-icon-table v-else-if="currentDisplayMode == displayModes.MODE_CARDS"></b-icon-table>
35
35
  </b-button>
36
36
 
37
37
  <div class="crud-search m-0" v-if="showSearch">
@@ -94,6 +94,16 @@ export default {
94
94
  sidebarVisible() {
95
95
  // Acceder directamente al componente padre para obtener reactividad
96
96
  return this.$parent ? this.$parent.filterSidebarOpen : this.filterSidebarOpen;
97
+ },
98
+ currentDisplayMode() {
99
+ if (!this.displayMode) return 1;
100
+ if (this.displayMode.value !== undefined) {
101
+ return this.displayMode.value;
102
+ }
103
+ if (typeof this.displayMode === 'function') {
104
+ return this.displayMode();
105
+ }
106
+ return this.displayMode;
97
107
  }
98
108
  },
99
109
  methods: {
@@ -1,6 +1,10 @@
1
1
  <template>
2
- <div v-if="displayMode == displayModes.MODE_KANBAN">
3
- <KanbanBoard />
2
+ <div v-if="currentDisplayMode == displayModes.MODE_KANBAN">
3
+ <KanbanBoard>
4
+ <template v-for="(slot, name) in $scopedSlots" v-slot:[name]="slotProps">
5
+ <slot :name="name" v-bind="slotProps" />
6
+ </template>
7
+ </KanbanBoard>
4
8
  </div>
5
9
  </template>
6
10
 
@@ -15,6 +19,18 @@ export default {
15
19
  inject: [
16
20
  'displayMode',
17
21
  'displayModes'
18
- ]
22
+ ],
23
+ computed: {
24
+ currentDisplayMode() {
25
+ if (!this.displayMode) return 1;
26
+ if (this.displayMode.value !== undefined) {
27
+ return this.displayMode.value;
28
+ }
29
+ if (typeof this.displayMode === 'function') {
30
+ return this.displayMode();
31
+ }
32
+ return this.displayMode;
33
+ }
34
+ }
19
35
  };
20
36
  </script>