summaryrefslogtreecommitdiffstats
path: root/packages/kadira-flow-router/client/triggers.js
blob: 7733332ca5137cae797274629df7db272b1ae3aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// a set of utility functions for triggers

Triggers = {};

// Apply filters for a set of triggers
// @triggers - a set of triggers
// @filter - filter with array fileds with `only` and `except` 
//           support only either `only` or `except`, but not both
Triggers.applyFilters = function(triggers, filter) {
  if(!(triggers instanceof Array)) {
    triggers = [triggers];
  }

  if(!filter) {
    return triggers;
  }

  if(filter.only && filter.except) {
    throw new Error("Triggers don't support only and except filters at once");
  }

  if(filter.only && !(filter.only instanceof Array)) {
    throw new Error("only filters needs to be an array");
  }

  if(filter.except && !(filter.except instanceof Array)) {
    throw new Error("except filters needs to be an array");
  }

  if(filter.only) {
    return Triggers.createRouteBoundTriggers(triggers, filter.only);
  }

  if(filter.except) {
    return Triggers.createRouteBoundTriggers(triggers, filter.except, true);
  }

  throw new Error("Provided a filter but not supported");
};

//  create triggers by bounding them to a set of route names
//  @triggers - a set of triggers 
//  @names - list of route names to be bound (trigger runs only for these names)
//  @negate - negate the result (triggers won't run for above names)
Triggers.createRouteBoundTriggers = function(triggers, names, negate) {
  var namesMap = {};
  _.each(names, function(name) {
    namesMap[name] = true;
  });

  var filteredTriggers = _.map(triggers, function(originalTrigger) {
    var modifiedTrigger = function(context, next) {
      var routeName = context.route.name;
      var matched = (namesMap[routeName])? 1: -1;
      matched = (negate)? matched * -1 : matched;

      if(matched === 1) {
        originalTrigger(context, next);
      }
    };
    return modifiedTrigger;
  });

  return filteredTriggers;
};

//  run triggers and abort if redirected or callback stopped
//  @triggers - a set of triggers 
//  @context - context we need to pass (it must have the route)
//  @redirectFn - function which used to redirect 
//  @after - called after if only all the triggers runs
Triggers.runTriggers = function(triggers, context, redirectFn, after) {
  var abort = false;
  var inCurrentLoop = true;
  var alreadyRedirected = false;

  for(var lc=0; lc<triggers.length; lc++) {
    var trigger = triggers[lc];
    trigger(context, doRedirect, doStop);

    if(abort) {
      return;
    }
  }

  // mark that, we've exceeds the currentEventloop for
  // this set of triggers.
  inCurrentLoop = false;
  after();

  function doRedirect(url, params, queryParams) {
    if(alreadyRedirected) {
      throw new Error("already redirected");
    }

    if(!inCurrentLoop) {
      throw new Error("redirect needs to be done in sync");
    }

    if(!url) {
      throw new Error("trigger redirect requires an URL");
    }

    abort = true;
    alreadyRedirected = true;
    redirectFn(url, params, queryParams);
  }

  function doStop() {
    abort = true;
  }
};