PageSourceSearch

https://web-cdn.spaces.hightail.com/client/14.56.18/router.js

js hightail.com collected 2026-09-24 08:29:11 UTC 19,357 bytes, 502 lines download raw bytes

1/**
2 * Router
3 *
4 * @since 10.1.0
5 *
6 * @copyright (c) 2017 Hightail Inc. All Rights Reserved
7 */
8'use strict';
9
10wilson.router(['$window', '$q', '$location', '$rootScope', '$route', '$timeout', 'ObjectStoreService', 'AuthService', 'TrackingService', 'UrlUtility', 'InterruptService', 'localStorageService', 'EarlyAccessService', 'ChatWidgetService', 'CookieService', 'SubscriptionResource',
11  function($window, $q, $location, $rootScope, $route, $timeout, ObjectStoreService, AuthService, TrackingService, UrlUtility, InterruptService, localStorageService, EarlyAccessService, ChatWidgetService, CookieService, SubscriptionResource) {
12
13    var DEFAULT_TRANSFER_ROUTE  = '/dashboard';
14    var MIGRATED_TRANSFER_ROUTE = '/send';
15
16    var previousParams          = {};
17    var previousPath            = null;
18    var libraryCache            = {};
19
20
21    //   ____       _            _         __  __      _   _               _
22    //  |  _ \ _ __(_)_   ____ _| |_ ___  |  \/  | ___| |_| |__   ___   __| |___
23    //  | |_) | '__| \ \ / / _` | __/ _ \ | |\/| |/ _ \ __| '_ \ / _ \ / _` / __|
24    //  |  __/| |  | |\ V / (_| | ||  __/ | |  | |  __/ |_| | | | (_) | (_| \__ \
25    //  |_|   |_|  |_| \_/ \__,_|\__\___| |_|  |_|\___|\__|_| |_|\___/ \__,_|___/
26    //
27    // region private methods
28
29
30    /**
31     * Handle any prerequisite logic prior to processing a route.
32     *
33     * @returns {angular.IPromise<T>}
34     */
35    function handleRoutePrereqs() {
36      // Facilitate possible early access, promise is resolved if no redirect is necessary
37      return AuthService.reconcileSessionUser().then(EarlyAccessService.facilitateEarlyAccess);
38    }
39
40
41    /**
42     * Method to process a given route change and handle any necessary redirect/storage/setup
43     * required to fulfill the route.  Also handles any actions necessary to fulfill component-level routing.
44     *
45     * @param currentRoute
46     * @param routeOptions
47     * @param routeInfo
48     * @returns {angular.IPromise<T>}
49     */
50    function processRoute(currentRoute, routeOptions, routeInfo) {
51      var routeLocal = true;
52
53      wilson.log.info('Handling Route Change - ' + currentRoute);
54      $rootScope.$broadcast('showLeftNavigationPannel', routeInfo);
55
56
57      // Handle any possible redirect that is required
58      var redirectInfo = getRouteRedirect(currentRoute, routeOptions);
59
60      if (redirectInfo) {
61
62        // Set Original Route to localStorage if it exists
63        if (redirectInfo.originalRoute) { localStorageService.set('originalRoute', redirectInfo.originalRoute); }
64
65        if (redirectInfo.hardRedirect) {
66          $window.location.href = redirectInfo.url;
67        } else {
68          $location.replace();
69          $location.path(redirectInfo.url);
70        }
71
72        return $q.reject();
73      }
74
75      // If we are ignoringLocalRoutes and we already have the component loaded, we can continue
76      if (routeOptions.ignoreLocalRouting && wilson.getActivePage() === routeInfo.component) {
77
78        // Check if our new route matches our current one
79        var routeMatchesCurrent = _.every(previousParams, function every(value, key) {
80          return $route.current.params[key] === value;
81        });
82        var samePath = previousPath && $location.path() === previousPath;
83
84        // If our unique route characteristics match up, then we know its time to ignore local routing and handle
85        if (routeMatchesCurrent) {
86          routeLocal = false;
87
88          // In the next process tick, broadcast a navigation event down the rootScope for handling
89          if (!samePath) {
90            $timeout(function() {
91              $rootScope.$broadcast('navigate', wilson.routeInfo);
92
93              // Fire artificial page tracking events
94              TrackingService.page($location.path(), routeInfo.component).track();
95            });
96          }
97        }
98
99      }
100
101      if (wilson.config.chatWidgetEnabled) {
102        // Hide chat for all routes except /update-billing /dashboard and /all-spaces
103        if (_.startsWith(currentRoute, '/update-billing')) {
104          ChatWidgetService.showChat();
105        } else if (!_.startsWith(currentRoute, '/dashboard') && !_.startsWith(currentRoute, '/all-spaces')) {
106          ChatWidgetService.hideChat();
107        } else if ($rootScope.authedUser.loggedIn &&
108          ($rootScope.authedUser.isTrialParticipant ||
109            ($rootScope.authedUser.subscription &&
110              ($rootScope.authedUser.subscription.planId === 'pl-00000000-0000-0000-0000-000000000003' ||
111                $rootScope.authedUser.subscription.planId === 'pl-00000000-0000-0000-0000-000000000004' ||
112                $rootScope.authedUser.subscription.planId === 'pl-00000000-0000-0000-0000-000000000005' ||
113                $rootScope.authedUser.subscription.planId === 'pl-00000000-0000-0000-0000-000000000006')))) {
114          ChatWidgetService.showChat();
115        } else {
116          ChatWidgetService.hideChat();
117        }
118      }
119
120      // Update our previous component reference and keep a reference to our previous params/path
121      previousParams = _.pick($route.current.params, routeInfo.options.uniqueParams || {});
122      previousPath   = $location.path();
123
124      // If we are routing locally and there are any open interrupts, we should close them
125      if (routeLocal && $rootScope.interruptActive) { InterruptService.clearAll(); }
126
127      // Now update our page title if we are not going to route locally (as this step will be skipped if we don't!)
128      if (!routeLocal) { $rootScope.page.title = _.template(routeInfo.title)($route.current.params); }
129
130      return routeLocal ? $q.when() : $q.reject();
131    }
132
133    /**
134     * Populate a redirect with the param values from the current route
135     * @param redirect
136     * @returns {String} - The redirect with param values inserted
137     */
138    function getRedirectWithParams(redirect) {
139      return _.map(redirect.split('/'), function(path) {
140        var paramRegex = /:\w+$/;
141        var param = path.match(paramRegex);
142
143        if (param) { param = param[0].substring(1); }
144
145        if (wilson.routeInfo[param]) {
146          return path.replace(paramRegex, wilson.routeInfo[param]);
147        } else {
148          return path;
149        }
150      }).join('/');
151    }
152
153    /**
154     * Get any supported redirect that is required from the given routeOptions
155     * @param currentRoute
156     * @param routeOptions
157     * @returns {Object} - An object defining the supported redirect (null if no redirect required)
158     */
159    function getRouteRedirect(currentRoute, routeOptions) {
160      var user = AuthService.bindUser();
161      if (user.authType && 'none' !== user.authType.toLowerCase() && false === user.verified && !routeOptions.allowUnverified) {
162        return {
163          url: '/verification',
164          hardRedirect: true
165        };
166      }
167
168      // Bad standing redirect
169      if (wilson.config.badstandingInterruptEnabled && !routeOptions.overrideBadStandingRedirect
170        && user.subscription && user.subscription.goodStanding === false
171        && (user.subscription.planId !== SubscriptionResource.supportedPlans.LITE.id
172         && user.subscription.planId !== SubscriptionResource.supportedPlans.CORE.id)) {
173            return {
174              url: '/update-billing',
175              hardRedirect: true
176            };
177      }
178
179      var hasClosedSeatLimitWarning = localStorageService.get('hasClosedSeatLimitWarning');
180      if (hasClosedSeatLimitWarning && (Number(hasClosedSeatLimitWarning) < (Date.now() - 24 * 60 * 60 * 1000))) {
181        localStorageService.remove('hasClosedSeatLimitWarning');
182      }
183
184      // retention over seat limit redirect
185      if (user.subscription && user.subscription.overSeatsLimitStatus === 'WARN' &&
186        !user.subscription.isPendingCancellation() &&
187        !routeOptions.overrideSeatLimitRedirect &&
188        !hasClosedSeatLimitWarning) {
189        return {
190          url: '/seat-limit',
191          hardRedirect: true
192        };
193      } else if (user.subscription && user.subscription.overSeatsLimitStatus === 'ERROR' &&
194        !routeOptions.overrideSeatLimitRedirect) {
195        return {
196          url: '/seat-limit',
197          hardRedirect: true
198        };
199      }
200
201      // 2FA Notification redirect if force-enable is on
202      if (!user.isSAMLUser && !user.oAuthProvider && !user.twoFactorEnabled && !routeOptions.overrideTwoFactorRedirect && user.getOrgPolicyValue('twoFactorAuthType') === 'ENABLED') {
203          if (user.getOrgPolicyValue('twoFactorAuthEffectiveDate') && moment(user.getOrgPolicyValue('twoFactorAuthEffectiveDate')).isSameOrBefore()) {
204            return {
205              url: '/two-factor-notification',
206              hardRedirect: true
207            };
208          } else if (!CookieService.get('2FAReminderDate') || moment(CookieService.get('2FAReminderDate')).isSameOrBefore()) {
209            $timeout(function() {
210              $rootScope.$broadcast('interrupt:two-factor-notification');
211            });
212          }
213      }
214
215      // Check if there is any pattern matching needed on this route
216      if (routeOptions.invalidPatternRedirect && !checkParamPatternsOnRoute($route.current.params, routeOptions)) {
217        var invalidRedirectTpl = _.template(routeOptions.invalidPatternRedirect);
218        return { url: invalidRedirectTpl($route.current.params) };
219      }
220
221      // Forwarding
222      if (routeOptions.forward)       {
223        // Template forward with route params
224        var forwardTpl = _.template(routeOptions.forward);
225        return { url: forwardTpl($route.current.params) };
226      }
227
228      // Hard Redirect
229      if (routeOptions.hardRedirect)  { return { url: routeOptions.hardRedirect, hardRedirect: true }; }
230
231      // Logged in redirect
232      if (routeOptions.loggedInRedirect && AuthService.hasUserSession()) {
233        return { url: getRedirectWithParams(routeOptions.loggedInRedirect) };
234      }
235
236      // Logged in hard redirect
237      if (routeOptions.loggedInHardRedirect && AuthService.hasUserSession()) { return { url: getRedirectWithParams(routeOptions.loggedInHardRedirect), hardRedirect: true }; }
238
239      // Logged out redirect
240      if (routeOptions.loggedOutRedirect && !AuthService.hasUserSession()) {
241        return { url: getRedirectWithParams(routeOptions.loggedOutRedirect), originalRoute: (routeOptions.storeOriginalRoute ? $location.url() : null) };
242      }
243
244      // Logged out corporate site redirect
245      if (routeOptions.loggedOutCorporateSiteRedirect && !AuthService.hasUserSession()) {
246        return {
247          url: (wilson.config.corporateWebBaseUrl + routeOptions.loggedOutCorporateSiteRedirect),
248          originalRoute: (routeOptions.storeOriginalRoute ? $location.path() : null),
249          hardRedirect: true
250        };
251      }
252
253      // Corporate Site Redirect
254      if (routeOptions.corporateSiteRedirect) {
255        return {
256          url: (wilson.config.corporateWebBaseUrl + routeOptions.corporateSiteRedirect),
257          originalRoute: (routeOptions.storeOriginalRoute ? $location.path() : null),
258          hardRedirect: true
259        };
260      }
261
262      // SignUp redirects
263      if (routeOptions.loggedOutSignUpRedirect && AuthService.hasAnonymousSession()) {
264        if (wilson.config.signupsInMarketing) {
265          return {
266            url: (wilson.config.corporateWebBaseUrl + (routeOptions.marketingPromoUrl? getRedirectWithParams(routeOptions.marketingPromoUrl): getRedirectWithParams(routeOptions.loggedOutSignUpRedirect))),
267            hardRedirect: true
268          };
269        } else if (currentRoute !== routeOptions.loggedOutSignUpRedirect) {
270          return {
271            url: getRedirectWithParams(routeOptions.loggedOutSignUpRedirect),
272            hardRedirect: true
273          };
274        }
275      }
276
277      if (routeOptions.loggedInSignUpRedirect && AuthService.hasUserSession()) {
278        return {
279          url: getRedirectWithParams(routeOptions.loggedInSignUpRedirect),
280          hardRedirect: true
281        };
282      } else if (routeOptions.loggedInSignUpRedirect && !AuthService.hasUserSession() && wilson.config.signupsInMarketing) {
283        return {
284          url: '/404',
285          hardRedirect: true
286        };
287      }
288
289      // User property redirects
290      if (routeOptions.userPropertyRedirects) {
291        var redirect = null;
292        _.forEach(routeOptions.userPropertyRedirects, function(userPropertyRedirect) {
293          if (redirect || (userPropertyRedirect.excludeAdmin && user.isOrgAdmin())) {
294            return;
295          }
296          if (userPropertyRedirect.not && _.first(_.at(user, userPropertyRedirect.path)) !== userPropertyRedirect.value) {
297              redirect = {
298                url: userPropertyRedirect.url,
299                hardRedirect: true
300              }
301          } else if (!userPropertyRedirect.not && _.first(_.at(user, userPropertyRedirect.path)) === userPropertyRedirect.value) {
302              redirect = {
303                url: userPropertyRedirect.url,
304                hardRedirect: true
305              }
306            }
307        });
308        if (redirect) {
309          return redirect;
310        }
311      }
312
313      // Privilege Check
314      if (routeOptions.privilegeRequired) {
315        var req = routeOptions.privilegeRequired;
316
317        // Check for org privilege to go into admin console. If he does NOT have organization AND the required privilege
318        if (!AuthService.hasUserSession() || !(user.subscription.organization && _.intersection(user.subscription.organization.privileges, req.privilege).length > 0) ) {
319          return { url: req.redirect };
320        }
321      }
322
323      if (routeOptions.teamsPrivilegeRequired) {
324        var req = routeOptions.teamsPrivilegeRequired;
325
326        // Check for org privilege to go into Teams tab. If the user does NOT have organization AND the required privilege
327          if (!AuthService.hasUserSession() || !user.hasTeamAccess()) {
328            return { url: req.redirect };
329          }
330      }
331
332      // Required Inlet Type check
333      if (routeOptions.requiredInletType && AuthService.hasUserSession()) {
334        if (routeOptions.requiredInletType !== user.inletType) { return { url: routeOptions.inletTypeRedirect || '/' }; }
335      }
336
337      // Required entitlement check
338      if (routeOptions.requiredEntitlement && AuthService.hasUserSession()) {
339        if (!user.entitlements[routeOptions.requiredEntitlement]) { return { url: routeOptions.entitlementRedirect || '/' }; }
340      }
341
342      // Hide nav bar for routes that don't support it.
343      if (!routeOptions.hasNavigation) {
344        $('.static-nav-bar').css('display', 'none');
345      }
346
347      return null;
348    }
349
350    function isParamPatternValid(paramValue, pattern) {
351      if (!_.isString(paramValue)) { return false; }
352
353      var regex = new RegExp(pattern);
354
355      return regex.test(paramValue);
356    }
357
358    function checkParamPatternsOnRoute(params, routeOptions) {
359      // If no regex patterns need to be enforced, there's no issue :)
360      if (!routeOptions.paramRegexPatterns) { return true; }
361
362      return _.every(routeOptions.paramRegexPatterns, function(pattern, routeParam) {
363        return isParamPatternValid(params[routeParam], pattern);
364      });
365    }
366
367    // endregion
368
369
370    //  ____                  _            __  __      _   _               _
371    // / ___|  ___ _ ____   _(_) ___ ___  |  \/  | ___| |_| |__   ___   __| |___
372    // \___ \ / _ \ '__\ \ / / |/ __/ _ \ | |\/| |/ _ \ __| '_ \ / _ \ / _` / __|
373    //  ___) |  __/ |   \ V /| | (_|  __/ | |  | |  __/ |_| | | | (_) | (_| \__ \
374    // |____/ \___|_|    \_/ |_|\___\___| |_|  |_|\___|\__|_| |_|\___/ \__,_|___/
375    //
376    // region service methods
377
378
379    /**
380     * Handle top level route change in the application (i.e. URL navigation)
381     *
382     * Performs any application-specific logic necessary for fulfilling an angular route.
383     *
384     * Returns a promise that is resolve if angular should continue its routing, and rejected
385     * if it should stop routing.
386     *
387     * @param currentRoute
388     * @param routeOptions
389     * @param routeInfo
390     * @returns {*|Promise}
391     */
392    function handleRouteChange(currentRoute, routeOptions, routeInfo) {
393      return handleRoutePrereqs().then(function() {
394        return processRoute(currentRoute, routeOptions, routeInfo);
395      }, $q.reject);
396    }
397
398
399    /**
400     * Method to Asynchronously resolve any js dependencies that are needed for this client-side route
401     * to be resolved by angular.
402     *
403     * @param routeInfo
404     */
405    function loadDependencies(routeInfo) {
406      // Load any needed javascript dependencies
407      var config          = wilson.config;
408      var host            = config.cdn ? (config.cdn.protocol + '://' + config.cdn.host) : '';
409      var versionedAssets = config.app.useVersionedAssets;
410      var options         = routeInfo.options || {};
411      var promises        = [];
412
413      _.each(options.jsDependencies, function(scriptPath) {
414        var preparedScriptPath = scriptPath;
415
416        if (versionedAssets) { preparedScriptPath = scriptPath.replace(config.app.assetPath, config.app.versionedAssetPath); }
417
418        if (!libraryCache[preparedScriptPath]) {
419          var scriptPromise = $q.defer();
420
421          $.getScript(host + preparedScriptPath).done(function() {
422            libraryCache[preparedScriptPath] = true;
423            scriptPromise.resolve();
424          }).fail(scriptPromise.reject);
425
426          promises.push(scriptPromise.promise);
427        }
428      });
429
430      // Resolve after all script dependencies are loaded
431      return $q.all(promises);
432    }
433
434
435    /**
436     * Attempts to provision a new session if we do not have one yet. If we do have a session,
437     * check if that session is valid.
438     *
439     * @method loadSession
440     *
441     * @return {Promise} - A Promise which will resolve when we have a session or our current session is deemed valid
442     */
443    function loadSession() {
444      return $q.when().then(function() {
445        // If there is no session, then let's provision one
446        if (!AuthService.hasSession()) {
447          return AuthService.provisionNewSession().then(function() {  // Mark tracking first touch
448            $timeout(function() {
449              var firstTouch = localStorageService.get('firstTouch');
450              localStorageService.set('firstTouch',  1);
451              if (!firstTouch) { $rootScope.$broadcast('firstTouch'); }
452            }, 500);
453          });
454        } else {
455          // Ok, we have a session, but is it valid?
456          AuthService.checkSession();
457        }
458      });
459    }
460
461
462    /**
463     * Translate and template the route title.
464     *
465     * This method should apply any appropriate templating and/or tranform to the route.title.
466     *
467     * @param routeTitle
468     * @returns {*}
469     */
470    function getTitleText(routeTitle) {
471      // Update page title
472      if (routeTitle) {
473        var content = 'Hightail Spaces';
474
475        try {
476          content = _.template(routeTitle)($route.current.params);
477        } catch (err) { }
478
479        return content;
480      }
481
482      return false;
483    }
484
485    // endregion
486
487
488    /************************************/
489    /******** SERVICE INTERFACE *********/
490    /************************************/
491    var service = {
492      handleRouteChange:  handleRouteChange,
493
494      loadDependencies:   loadDependencies,
495      loadSession:        loadSession,
496
497      getTitleText:       getTitleText
498    };
499
500    return service;
501  }
502]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.