@phila/layerboard 2.2.0 → 3.0.0-beta.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.
@@ -1,264 +0,0 @@
1
- <template>
2
- <div
3
- id="topic-panel-container"
4
- :class="topicPanelContainerClass"
5
- >
6
- <div
7
- v-show="!!this.$config.layerFilter"
8
- class="forms-header"
9
- >
10
- <!-- tags filter -->
11
- <form
12
- class="om-search-control-input-tags"
13
- @submit.prevent="handleTagsFilterFormX"
14
- @keydown="preventEnter"
15
- >
16
- <div class="input-group text-filter">
17
- <span class="input-group-label input-font">Filter:</span>
18
- <input
19
- type="text"
20
- class="input-type"
21
- @keyup="handleTagsFilterFormKeyup"
22
- >
23
- <!-- placeholder="Filter datasets" -->
24
- <div
25
- v-if="this.$store.state.layers.inputTagsFilter != ''"
26
- class="input-group-button"
27
- >
28
- <button class="om-search-control-button">
29
- <i class="fa fa-times fa-lg" />
30
- </button>
31
- </div>
32
- </div>
33
- </form>
34
- </div>
35
-
36
- <div
37
- id="topics-container"
38
- class="topics-container cell medium-cell-block-y"
39
- :style="topicsContainerStyle"
40
- >
41
- <topic-component-group
42
- :topic-components="appComponents"
43
- @handle-topic-header-click="handleTopicHeaderClick"
44
- />
45
- </div>
46
- </div>
47
- </template>
48
-
49
- <script>
50
-
51
- // import Checkbox from '@phila/vue-mapping/src/esri-leaflet/Checkbox.vue';
52
- // import Topic from '@phila/vue-comps/src/components/Topic.vue';
53
- import TopicComponentGroup from '@phila/vue-comps/src/components/TopicComponentGroup.vue';
54
-
55
- export default {
56
- components: {
57
- // Checkbox,
58
- TopicComponentGroup,
59
- // Topic,
60
- // Checkbox: () => import(/* webpackChunkName: "lbmp_pvm_Checkbox" */'@phila/vue-mapping/src/esri-leaflet/Checkbox.vue'),
61
- },
62
- data() {
63
- const data = {
64
- topicsContainerStyle: {
65
- 'overflow-y': 'auto',
66
- 'height': '100px',
67
- 'min-height': '100px',
68
- },
69
- };
70
- return data;
71
- },
72
- computed: {
73
- windowDim() {
74
- return this.$store.state.windowDimensions;
75
- },
76
- appComponents() {
77
- if (this.$config.components) {
78
- return this.$config.components;
79
- }
80
- // if no components, use a single 'checkbox-set'
81
- return [{ type: 'checkbox-set' }];
82
-
83
- },
84
- fullScreenMapEnabled() {
85
- return this.$store.state.fullScreenMapEnabled;
86
- },
87
- topicPanelContainerClass() {
88
- if (this.fullScreenMapEnabled) {
89
- return 'cell medium-1 small-order-2 medium-order-1';
90
- } else if (this.windowDim.width >= 950) {
91
- return 'cell medium-8 small-order-1 small-24 medium-order-2';
92
- }
93
- return 'cell medium-10 small-order-1 small-24 medium-order-2';
94
-
95
- },
96
- categories() {
97
- return this.$store.state.map.categories;
98
- },
99
- selectedCategory() {
100
- return this.$store.state.map.selectedCategory;
101
- },
102
- scale() {
103
- return this.$store.state.map.scale;
104
- },
105
- inputLayerFilter() {
106
- return this.$store.state.layers.inputLayerFilter;
107
- },
108
- inputTagsFilter() {
109
- return this.$store.state.layers.inputTagsFilter;
110
- },
111
- windowSize() {
112
- return this.$store.state.windowSize;
113
- },
114
- },
115
- watch: {
116
- windowDim(nextDim) {
117
- this.handleWindowResize(nextDim);
118
- },
119
- },
120
- methods: {
121
- handleTopicHeaderClick(nextTopic) {
122
- // console.log('TopicPanel handleTopicHeaderClick is running');
123
- this.$controller.handleTopicHeaderClick(nextTopic);
124
- },
125
- handleLayerFilterFormKeyup(e) {
126
- const input = e.target.value;
127
- this.$store.commit('setInputLayerFilter', input);
128
- },
129
- handleLayerFilterFormX(e) {
130
- e.target[0].value = '';
131
- this.$store.commit('setInputLayerFilter', '');
132
- },
133
- handleTagsFilterFormKeyup(e) {
134
- const input = e.target.value;
135
- // if (input.length >= 3) {
136
- this.$store.commit('setInputTagsFilter', input);
137
- // } else {
138
- // this.$store.commit('setInputLayerFilter', null);
139
- // }
140
- },
141
- handleTagsFilterFormX(e) {
142
- e.target[0].value = '';
143
- this.$store.commit('setInputTagsFilter', '');
144
- },
145
- didSelectCategory(e) {
146
- const selected = e.target.selectedIndex;
147
- this.$store.commit('setSelectedCategory', this.categories[selected]);
148
- },
149
- preventEnter(e) {
150
- if(e.keyCode === 13) {
151
- e.preventDefault();
152
- }
153
- },
154
- handleWindowResize(dim) {
155
- // console.log('TopicPanel handleWindowResize, dim:', dim);
156
- const windowHeight = window.innerHeight;
157
- const siteHeaderHeightNum = parseInt(document.getElementsByClassName('site-header')[0].getBoundingClientRect().height);
158
- const appFooterHeightNum = parseInt(document.getElementsByClassName('app-footer')[0].getBoundingClientRect().height);
159
- const datasetsButtonHeightNum = parseInt(document.getElementsByClassName('datasets-button')[0].getBoundingClientRect().height);
160
- const formsHeaderHeightNum = parseInt(document.getElementsByClassName('forms-header')[0].getBoundingClientRect().height);
161
- // console.log('TopicPanel handleWindowResize is running, formsHeaderHeightNum:', formsHeaderHeightNum);
162
- let topicsHeight = windowHeight - siteHeaderHeightNum - appFooterHeightNum - datasetsButtonHeightNum - formsHeaderHeightNum;
163
- // console.log('topicsHeight:', topicsHeight);
164
-
165
- // let topicsHeight = dim.height;
166
-
167
- this.topicsContainerStyle.height = topicsHeight.toString() + 'px';
168
- this.topicsContainerStyle['min-height'] = topicsHeight.toString() + 'px';
169
- this.topicsContainerStyle['overflow-y'] = 'auto';
170
- },
171
- },
172
- };
173
- </script>
174
-
175
- <style scoped>
176
- .forms-header {
177
- padding: 5px;
178
- /*this keeps the box shadow over the scrollable part of the panel*/
179
- position: relative;
180
- z-index: 1;
181
- -webkit-box-shadow: 0px 5px 7px -2px rgba(0,0,0,0.18);
182
- -moz-box-shadow: 0px 5px 7px -2px rgba(0,0,0,0.18);
183
- box-shadow: 0px 5px 7px -2px rgba(0,0,0,0.18);
184
- }
185
-
186
- .input-type {
187
- margin-bottom: 0px;
188
- }
189
-
190
- .input-select {
191
- margin-bottom: 0px;
192
- }
193
-
194
- .input-group {
195
- height: 40px !important;
196
- margin-top: 6px;
197
- margin-bottom: 6px;
198
- }
199
-
200
- .input-group-label {
201
- border-right: 1px;
202
- border-style: solid;
203
- padding-left: 8px;
204
- padding-right: 8px;
205
- padding-top: 0px;
206
- padding-bottom: 0px;
207
- height: 20px !important;
208
- }
209
-
210
- .topics-container {
211
- padding: 20px;
212
- overflow-y: auto;
213
- }
214
-
215
- .topics-container {
216
- /* height: calc(100vh - 220px); */
217
- /* height: calc(100vh - 268px); */
218
-
219
- /* height: calc(100vh - 218px); */
220
-
221
- /* height: calc(100vh - 170px); */
222
- height: calc(100vh - 110px);
223
- }
224
-
225
- /* @media screen and (max-width: 40em) { */
226
- @media screen and (max-width: 750px) {
227
- .topics-container {
228
- /* height: calc(100vh - 256px); */
229
-
230
- /* height: calc(100vh - 300px); */
231
- height: calc(100vh - 140px);
232
- }
233
- }
234
-
235
- .om-search-control-container {
236
- border-radius: 2px;
237
- box-shadow:0 2px 4px rgba(0,0,0,0.2),0 -1px 0px rgba(0,0,0,0.02);
238
- margin-top: 10px;
239
- margin-bottom: 10px;
240
- width: inherit;
241
- }
242
-
243
- .om-search-control-button {
244
- width: 40px;
245
- background: #ccc;
246
- float: right;
247
- }
248
-
249
- .om-search-control-input {
250
- font-family: 'Montserrat', 'Tahoma', sans-serif;
251
- font-size: 16px;
252
- }
253
-
254
- .om-search-control-input-tags {
255
- font-family: 'Montserrat', 'Tahoma', sans-serif;
256
- font-size: 16px;
257
- }
258
-
259
- .input-font {
260
- font-family: 'Montserrat', 'Tahoma', sans-serif;
261
- font-size: 16px;
262
- }
263
-
264
- </style>
@@ -1,58 +0,0 @@
1
- export default {
2
- computed: {
3
- // returns map markers as simple object with a geometry property, key,
4
- // and optional properties for symbology
5
- markers() {
6
- const markers = [];
7
- // geocoded address marker
8
- const geocodeGeom = this.geocodeGeom;
9
- if (geocodeGeom) {
10
- const latlng = [ ...geocodeGeom.coordinates ].reverse();
11
- const key = this.geocodeResult.properties.street_address;
12
- const color = '#2176d2';
13
- const markerType = 'geocode';
14
- const icon = {
15
- prefix: 'fas',
16
- icon: 'map-marker-alt',
17
- shadow: true,
18
- size: 50,
19
- };
20
- const addressMarker = { latlng, key, color, markerType, icon };
21
- markers.push(addressMarker);
22
- }
23
- return markers;
24
- },
25
- locationMarker() {
26
- const latlngArray = [ this.$store.state.map.location.lat, this.$store.state.map.location.lng ];
27
- const marker = {
28
- latlng: latlngArray,
29
- radius: 6,
30
- fillColor: '#ff3f3f',
31
- color: '#ff0000',
32
- weight: 1,
33
- opacity: 1,
34
- fillOpacity: 1.0,
35
- };
36
- return marker;
37
- },
38
- clickMarker() {
39
- const latlngArray = [ this.$store.state.map.clickMarkerLocation.lat, this.$store.state.map.clickMarkerLocation.lng ];
40
- const marker = {
41
- latlng: latlngArray,
42
- radius: 6,
43
- fillColor: '#ff3f3f',
44
- color: '#ff0000',
45
- weight: 1,
46
- opacity: 1,
47
- fillOpacity: 1.0,
48
- icon: {
49
- prefix: 'fas',
50
- icon: 'map-marker-alt',
51
- shadow: true,
52
- size: 50,
53
- },
54
- };
55
- return marker;
56
- },
57
- },
58
- };
package/src/fa.js DELETED
@@ -1,15 +0,0 @@
1
-
2
- import * as faComps from '@phila/vue-comps/src/fa.js';
3
- import * as faMapping from '@phila/vue-mapping/src/fa.js';
4
-
5
- // Font Awesome Icons
6
- import { library } from '@fortawesome/fontawesome-svg-core';
7
- import { faSpinner } from '@fortawesome/free-solid-svg-icons/faSpinner';
8
- import { faInfoCircle } from '@fortawesome/free-solid-svg-icons/faInfoCircle';
9
- import { faCheckSquare } from '@fortawesome/free-solid-svg-icons/faCheckSquare';
10
- import { faFilter } from '@fortawesome/free-solid-svg-icons/faFilter';
11
- import { faSquare } from '@fortawesome/free-regular-svg-icons/faSquare';
12
- import { faCircle } from '@fortawesome/free-regular-svg-icons/faCircle';
13
- library.add(faSpinner, faInfoCircle, faCheckSquare, faFilter, faSquare, faCircle);
14
-
15
- export default library;
package/src/main.js DELETED
@@ -1,93 +0,0 @@
1
- /*
2
- .____ ___. .___
3
- | | _____ ___.__. __________\_ |__ _________ _______ __| _/
4
- | | \__ \< | |/ __ \_ __ \ __ \ / _ \__ \\_ __ \/ __ |
5
- | |___ / __ \\___ \ ___/| | \/ \_\ ( <_> ) __ \| | \/ /_/ |
6
- |_______ (____ / ____|\___ >__| |___ /\____(____ /__| \____ |
7
- \/ \/\/ \/ \/ \/ \/
8
- */
9
-
10
-
11
- import Vue from 'vue';
12
- import axios from 'axios';
13
- import createStore from './store';
14
- import configMixin from './util/config-mixin';
15
- import App from './App.vue';
16
- import Layerboard from './components/Layerboard.vue';
17
- import mergeDeep from './util/merge-deep';
18
-
19
- import * as faAll from './fa.js';
20
- import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
21
-
22
- import controllerMixin from '@phila/vue-datafetch/src/controller.js';
23
-
24
- import Router from 'vue-router';
25
-
26
- import * as L from 'leaflet';
27
- import * as esri from 'esri-leaflet';
28
- L.esri = esri;
29
- import * as rend from 'esri-leaflet-renderers';
30
- L.esri.Renderers = rend;
31
- import 'esri-leaflet-legend/dist/esri-leaflet-legend-compat-src-edit.js';
32
- import 'Leaflet-PointInPolygon/wise-leaflet-pip.js';
33
-
34
- function initLayerboard(clientConfig, secondFile) {
35
- const baseConfigUrl = clientConfig.baseConfig;
36
-
37
- // get base config
38
- return axios.get(baseConfigUrl).then(response => {
39
- const data = response.data;
40
- const baseConfigFn = eval(data);
41
- const { gatekeeperKey } = clientConfig;
42
- const baseConfig = baseConfigFn({ gatekeeperKey });
43
-
44
- // deep merge base config and client config
45
- let config = mergeDeep(baseConfig, clientConfig);
46
- if (secondFile) {
47
- config = mergeDeep(config, secondFile);
48
- }
49
-
50
- config.router.pattern = 'address-and-topic';
51
-
52
- // make config accessible from each component via this.$config
53
- Vue.use(configMixin, config);
54
-
55
- // create store
56
- const store = createStore(config);
57
-
58
- Vue.use(Router);
59
- let router = new Router({
60
- mode: 'history',
61
- routes: [
62
- {
63
- path: '/:address',
64
- name: 'address-only',
65
- },
66
- ],
67
- });
68
-
69
- // mix in controller
70
- Vue.use(controllerMixin, { config, store, router });
71
-
72
- Vue.component('font-awesome-icon', FontAwesomeIcon);
73
-
74
- const customComps = config.customComps || [];
75
- // console.log('mapboard main.js, customComps:', customComps);
76
- for (let key of Object.keys(customComps)) {
77
- Vue.component(key, customComps[key]);
78
- }
79
-
80
- // mount main vue
81
- const vm = new Vue({
82
- el: config.el || '#layerboard',
83
- render: (h) => h(App),
84
- router,
85
- store,
86
- });
87
-
88
- }, response => {
89
- console.error('AXIOS ERROR loading base config');
90
- });
91
- }
92
-
93
- export default initLayerboard;
package/src/store.js DELETED
@@ -1,229 +0,0 @@
1
- import Vue from 'vue';
2
- import Vuex from 'vuex';
3
- import isMobileDevice from './util/is-mobile-device';
4
- import pvdStore from '@phila/vue-datafetch/src/store.js';
5
- import pvmStore from '@phila/vue-mapping/src/store.js';
6
- import pvcStore from '@phila/vue-comps/src/store.js';
7
- import mergeDeep from './util/merge-deep';
8
-
9
- // when you load vuex from a script tag this seems to happen automatically
10
- Vue.use(Vuex);
11
-
12
- // function createStore(config, bennyEndpoints, bennyRepresentation) {
13
- function createStore(config) { //}, bennyEndpoints, bennyRepresentation) {
14
- // const modals = pvdStore.createModals(config);
15
- const sources = pvdStore.createSources(config);
16
-
17
- const initialState = {
18
- sources,
19
- isMobileOrTablet: isMobileDevice(),
20
- fullScreen: {
21
- mapOnly: false,
22
- topicsOnly: false,
23
- },
24
- fullScreenMapEnabled: false,
25
- bennyEndpoints: {},
26
- layers: {
27
- defaultLayers: [],
28
- layerUrls: {},
29
- inputLayerFilter: '',
30
- inputTagsFilter: '',
31
- },
32
- map: {
33
- tiledLayers: [],
34
- mode: 'identifyFeatures',
35
- scale: null,
36
- webMap: null,
37
- webMapUrlLayer: null,
38
- webMapActiveLayers: [],
39
- webMapDisplayedLayers: [],
40
- webMapLayersAndRest: [],
41
- intersectingFeatures: [],
42
- popupCoords: null,
43
- selectedPopupLayer: null,
44
- categories: [],
45
- selectedCategory: '',
46
- clickMarkerLocation: {
47
- lat: null,
48
- lng: null,
49
- },
50
- },
51
- candidates: [],
52
- addressEntered: null,
53
- didToggleTopicsOn: false,
54
- shouldShowTopics: true,
55
- shouldShowMap: true,
56
- windowDimensions: {
57
- height: 0,
58
- width: 0,
59
- },
60
- // windowWidth: 0,
61
- route: null,
62
- modals: {
63
- keys: config.modals,
64
- open: '',
65
- },
66
- };
67
-
68
- if (config.map) {
69
- if (config.map.initialImagery) {
70
- initialState.map.imagery = config.map.initialImagery;
71
- }
72
- if (config.map.overlaySelectControl) {
73
- if (config.map.overlaySelectControl.initialSelection) {
74
- initialState.map.selectedOverlay = config.map.overlaySelectControl.initialSelection;
75
- }
76
- }
77
- }
78
-
79
- const lb = {
80
- state: initialState,
81
- getters: {},
82
- mutations: {
83
- setTiledLayers(state, payload) {
84
- state.map.tiledLayers = payload;
85
- },
86
- setClickMarkerLocation(state, payload) {
87
- state.map.clickMarkerLocation = payload;
88
- },
89
- setMapMode(state, payload) {
90
- state.map.mode = payload;
91
- },
92
- setIsMobileOrTablet(state, payload) {
93
- state.isMobileOrTablet = payload;
94
- },
95
- setMapOnly(state, payload) {
96
- state.fullScreen.mapOnly = payload;
97
- },
98
- setTopicsOnly(state, payload) {
99
- state.fullScreen.topicsOnly = payload;
100
- },
101
- setFullScreenMapEnabled(state, payload) {
102
- state.fullScreenMapEnabled = payload;
103
- },
104
- setCategories(state, payload) {
105
- state.map.categories = payload;
106
- },
107
- setSelectedCategory(state, payload) {
108
- state.map.selectedCategory = payload;
109
- },
110
- setSelectedPopupLayer(state, payload) {
111
- state.map.selectedPopupLayer = payload;
112
- },
113
- setIntersectingFeatures(state, payload) {
114
- state.map.intersectingFeatures = payload;
115
- },
116
- setPopupCoords(state, payload) {
117
- state.map.popupCoords = payload;
118
- },
119
- setBennyEndpoints(state, payload) {
120
- state.bennyEndpoints = payload;
121
- },
122
- // setLocation(state, payload) {
123
- // state.map.location.lat = payload.lat;
124
- // state.map.location.lng = payload.lng;
125
- // },
126
- setWatchPositionOn(state, payload) {
127
- state.map.watchPositionOn = payload;
128
- },
129
- setDefaultLayers(state, payload) {
130
- state.layers.defaultLayers = payload;
131
- },
132
- setLayerUrls(state, payload) {
133
- state.layers.layerUrls = payload;
134
- },
135
- setInputLayerFilter(state, payload) {
136
- state.layers.inputLayerFilter = payload;
137
- },
138
- setInputTagsFilter(state, payload) {
139
- state.layers.inputTagsFilter = payload;
140
- },
141
- setMap(state, payload) {
142
- state.map.map = payload.map;
143
- },
144
- setWebMap(state, payload) {
145
- state.map.webMap = payload;
146
- },
147
- setWebMapUrlLayer(state, payload) {
148
- state.map.webMapUrlLayer = payload;
149
- },
150
- setWebMapActiveLayers(state, payload) {
151
- state.map.webMapActiveLayers = payload;
152
- },
153
- setWebMapDisplayedLayers(state, payload) {
154
- state.map.webMapDisplayedLayers = payload;
155
- },
156
- setWebMapLayersAndRest(state, payload) {
157
- state.map.webMapLayersAndRest = payload;
158
- },
159
- setWebMapLayersOpacity(state, payload) {
160
- // console.log('SETWEBMAPLAYERSOPACITY IS RUNNING', payload);
161
- // let opa = state.map.webMapLayersAndRest.filter(layer => layer.layerName === payload.layerName)[0].opacity
162
- // console.log('OPACITY BEFORE', state.map.webMapLayersAndRest.filter(layer => layer.title === payload.layerName)[0].opacity);
163
- state.map.webMapLayersAndRest.filter(layer => layer.title === payload.layerName)[0].opacity = payload.opa;
164
- // console.log('OPACITY AFTER', state.map.webMapLayersAndRest.filter(layer => layer.title === payload.layerName)[0].opacity);
165
- // return currentLayer[0];
166
- // console.log('SETWEBMAPLAYERSOPACITY FINISHED RUNNING');
167
- },
168
- setLegend(state, payload) {
169
- // console.log('METHOD layerboard store setLegend is running, payload:', payload);
170
- state.map.webMapLayersAndRest.filter(layer => layer.title === payload.layerName)[0].legend = payload.legend;
171
- },
172
- setMapScale(state, payload) {
173
- state.map.scale = payload;
174
- },
175
- // setBasemap(state, payload) {
176
- // state.map.basemap = payload;
177
- // },
178
- setDidToggleTopicsOn(state, payload) {
179
- state.didToggleTopicsOn = payload;
180
- },
181
- setShouldShowTopics(state, payload) {
182
- state.shouldShowTopics = payload;
183
- },
184
- setShouldShowMap(state, payload) {
185
- state.shouldShowMap = payload;
186
- },
187
- setWindowDimensions(state, payload) {
188
- state.windowDimensions = payload;
189
- },
190
- // setWindowWidth(state, payload) {
191
- // state.windowWidth = payload;
192
- // },
193
- setRoute(state, payload) {
194
- state.route = payload;
195
- },
196
- // setDidToggleModal(state, name) {
197
- // // console.log('setDidToggleModal, name:', name, 'open:', open);
198
- // // console.log('setDidToggleModal, name:', name);
199
- // // state.modals[name].open = open === null ? !state.modals[name].open : open
200
- // state.modals.open = name;
201
- // },
202
- setCandidates(state, payload) {
203
- state.candidates = payload;
204
- },
205
- setAddressEntered(state, payload) {
206
- state.addressEntered = payload;
207
- },
208
- },
209
- };
210
-
211
- // let mergeStore = mergeDeep(lb, pvdStore.store);
212
- let mergeStore = mergeDeep(pvcStore, pvdStore.store);
213
- mergeStore = mergeDeep(mergeStore, pvmStore);
214
- mergeStore = mergeDeep(mergeStore, lb);
215
-
216
- // reset the map center and zoom based on the config
217
- mergeStore.state.map.center = config.map.center;
218
- mergeStore.state.map.zoom = config.map.zoom;
219
- mergeStore.state.pictometry.map.center = config.map.center;
220
- mergeStore.state.pictometry.map.zoom = config.map.zoom;
221
-
222
- return new Vuex.Store({
223
- state: mergeStore.state,
224
- getters: mergeStore.getters,
225
- mutations: mergeStore.mutations,
226
- });
227
- }
228
-
229
- export default createStore;
@@ -1,10 +0,0 @@
1
- // shout out to airyland
2
- // https://github.com/airyland/vue-config/blob/master/index.js
3
-
4
- export default (Vue, config) => {
5
- Vue.mixin({
6
- created() {
7
- this.$config = config;
8
- },
9
- });
10
- };
@@ -1,11 +0,0 @@
1
- export default function () {
2
- const mobileOrTabletRegexA = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
3
- const mobileOrTabletRegexB = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
4
-
5
- // get the user agent and test against both regex patterns
6
- const userAgent = (navigator.userAgent || navigator.vendor || window.opera || '');
7
- const isMobileOrTablet = (mobileOrTabletRegexA.test(userAgent) ||
8
- mobileOrTabletRegexB.test(userAgent.substr(0,4)));
9
-
10
- return isMobileOrTablet;
11
- }