sports_db 0.2.3 → 0.2.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.
Files changed (32) hide show
  1. data/app/assets/javascripts/core/App.js +256 -0
  2. data/app/assets/javascripts/core/BaseView.js +13 -10
  3. data/app/assets/javascripts/core/History.js +2 -2
  4. data/app/assets/javascripts/core/Mock.js +7 -9
  5. data/app/assets/javascripts/core/Timer.js +3 -1
  6. data/app/assets/javascripts/core/View.js +2 -1
  7. data/app/assets/javascripts/core/utilities.js +8 -6
  8. data/app/assets/javascripts/plugins/articles.js +195 -0
  9. data/app/assets/javascripts/plugins/assert.js +1 -1
  10. data/app/assets/javascripts/plugins/calnav.js +2 -1
  11. data/app/assets/javascripts/plugins/collapsible.js +1 -0
  12. data/app/assets/javascripts/plugins/delayed_load.js +42 -0
  13. data/app/assets/javascripts/plugins/flash.js +1 -1
  14. data/app/assets/javascripts/plugins/jquery.zumobi-0.2.js +2 -3
  15. data/app/assets/javascripts/plugins/loading.js +1 -0
  16. data/app/assets/javascripts/plugins/resizeable.js +1 -0
  17. data/app/assets/javascripts/plugins/zepto.filterable.js +1 -13
  18. data/app/assets/javascripts/plugins/zepto.preload.js +2 -1
  19. data/app/assets/javascripts/plugins/zepto.tabs.js +3 -3
  20. data/app/assets/stylesheets/_articles.css.scss +153 -0
  21. data/app/assets/stylesheets/_base.css.scss +16 -1
  22. data/app/assets/stylesheets/_filterable.css.scss +3 -11
  23. data/app/assets/stylesheets/_gradients.css.scss +26 -0
  24. data/app/assets/stylesheets/_logo.css.scss +12 -0
  25. data/app/assets/stylesheets/_table_base.scss +40 -4
  26. data/app/assets/stylesheets/_table_collapsible.css.scss +24 -0
  27. data/app/assets/stylesheets/_tabs.css.scss +59 -0
  28. data/app/assets/stylesheets/_toolbar.css.scss +38 -0
  29. data/app/models/twitter.rb +5 -4
  30. data/app/views/shared/_articles.html.erb +66 -0
  31. data/lib/sports_db/version.rb +1 -1
  32. metadata +12 -2
@@ -0,0 +1,256 @@
1
+ var Application = Class.extend({
2
+ initialize: function() {
3
+
4
+ this.views = {};
5
+ this.params = {};
6
+ this.cache = {};
7
+ this.currentView = null;
8
+ this.clientCallbackHandlers = [];
9
+ this.wrapper = $('#view_wrapper');
10
+ this.buffer = $('#tmp');
11
+ this.footer = $('#footer');
12
+ this.client = {};
13
+ this.setClientParams();
14
+
15
+ // Get hash params
16
+ if (window.location.hash) {
17
+ this.params = $.serialize(location.hash.slice(1));
18
+ }
19
+
20
+ this.params = $.extend(window.CONFIG, this.params);
21
+
22
+ if (!this.params.clientSupportsClose) {
23
+ this.app_is_initializing = true;
24
+ }
25
+
26
+ // Start timer for game updates
27
+ // TODO only run this on game views
28
+ this.timer = new Timer($.proxy(this._fireTimerEvent, this), this.params.defaultTimer);
29
+ this.timer.start();
30
+ },
31
+ setClientParams: function() {
32
+ var q = $.deserialize( location.search.slice(1) );
33
+
34
+ $.assert(q.client, '`client` param is required.');
35
+ $.assert(q.version, '`version` param is required.');
36
+
37
+ this.client[q.client] = true;
38
+ // this.client.version_major = q.version.split('.')[0];
39
+ // this.client.version_minor = q.version.split('.')[1];
40
+
41
+ $('html').addClass('client_'+q.client);
42
+ },
43
+ // Fire a timer event
44
+ _fireTimerEvent: function() {
45
+ $(document.body).trigger('timerReachInterval');
46
+ },
47
+ // Call the view if the method exists
48
+ _onTimerReachedInterval: function() {
49
+ var view = this.currentView;
50
+ if (view && view._onTimerReachedInterval) {
51
+ view._onTimerReachedInterval.call(view);
52
+ }
53
+ },
54
+ // Register a view constructor under a specific name. If a link in the application has
55
+ // a parameter named "view" that points to a registered view name, then the `Application`
56
+ // will load that view. Optionally, you can pass in parameters, and these will be merged
57
+ // with any other parameters in the link that triggers view loading (parameters in the
58
+ // link override parameters in the registration binding). This allows you to bind the
59
+ // same view under different names with different parameters in order to parameterize
60
+ // its behavior.
61
+ //
62
+ // @param {Object} name
63
+ // @param {Object} constructor
64
+ // @param {Object} params (optional)
65
+ registerView: function(name, constructor, params) {
66
+ this.views[name] = { constructor: constructor, params: (params || {}) };
67
+ },
68
+ // Stash a view, i.e. change the url to something meaningful, but do not show the
69
+ // view based on this URL change. This is needed in cases where the device goes to
70
+ // sleep, and then reloads upon awakening.
71
+ stashView: function(params) {
72
+ $.assert(!this.params.clientSupportsClose, "App should not be using Application.stashView method");
73
+
74
+ // TODO kill this method in all apps. Need client support.
75
+ if (!this.params.clientSupportsClose) {
76
+ console.log('** Application.stashView');
77
+ var local = {};
78
+ local.view = params.view;
79
+ if (params.filter) { local.filter = params.filter; }
80
+ if (params.key) { local.key = params.key; }
81
+ local.stash = true;
82
+ var url = "#" + $.serialize(local);
83
+
84
+ document.location = url;
85
+ }
86
+ },
87
+ // Show a view based on the supplied parameters. The only required parameter is "view"
88
+ // which must reference a valid view name.
89
+ //
90
+ // @param {Object} params
91
+ showView: function(params) {
92
+ console.log('$$ Application.showView');
93
+ $.assert(params.view, 'No view specified (use /' + this.params.serverPath + '/#view=<viewName>)');
94
+
95
+ var binding = this.views[params.view];
96
+
97
+ $.assert(binding, params.view + ' is not a registered view name. Did you add it to views.js?');
98
+
99
+ // Merge binding parameters (from views.js) into any parameters that were supplied in the URL
100
+ for (var prop in binding.params) {
101
+ if (typeof params[prop] === 'undefined') {
102
+ params[prop] = binding.params[prop];
103
+ }
104
+ }
105
+ var view = new binding.constructor(params, this);
106
+ $.assert(view.create, params.view + ' does not have a create method and is not a valid view');
107
+
108
+ view.create(params);
109
+ },
110
+ // When the view is done initializing, it must call this method.
111
+ appendView: function(newView, cached) {
112
+ console.log('$$ Application.appendView');
113
+
114
+ // Save the scroll state of the old view
115
+ if (this.currentView) {
116
+ this.currentView.params.pageYOffset = window.pageYOffset;
117
+ }
118
+
119
+ // Re-assign this.currentView to the new view.
120
+ // Add it to the Application.cache.
121
+ this.currentView = this.cache[History.currentHash()] = newView;
122
+
123
+ // Mark the view as cached
124
+ if (cached) {
125
+ this.currentView.params.cached = true;
126
+ // NBA3 TODO use the param
127
+ $(newView.element).addClass('cached');
128
+ }
129
+
130
+ this.wrapper.empty().append(newView.element);
131
+
132
+ this.currentView._evaluateDataAttribute();
133
+
134
+ // Set scroll position.
135
+ window.scrollTo(0, (this.currentView.params.pageYOffset || 0));
136
+
137
+ // Reset metrics flag
138
+ this.currentView.params.hasSentMetrics = false;
139
+
140
+ // Allow the view to add behavior.
141
+ this.currentView.onViewChanged(this.currentView.params, this);
142
+
143
+ // Send Metrics
144
+ if (!this.currentView.params.skip_metrics === true) {
145
+ this.sendMetrics(this.currentView);
146
+ }
147
+
148
+ // Let plugins know about the view being appended
149
+ $(document.body).trigger('Application:viewAppended', [this.currentView.params]);
150
+
151
+ // Show the footer
152
+ if (this.footer) {
153
+ this.footer.show();
154
+ }
155
+
156
+ // Hide the loader
157
+ $.hideLoader();
158
+ },
159
+ sendMetrics: function(view) {
160
+ Client.notify({
161
+ 'action': view.getAction(),
162
+ 'buttons': view.getButtons(),
163
+ 'filter': view.getFilter(),
164
+ 'metric': view.getMetric(),
165
+ 'refreshAd': view.shouldRefreshAd(),
166
+ 'section': view.getSection(),
167
+ 'title': view.getTitle()
168
+ });
169
+ view.params.hasSentMetrics = true;
170
+ },
171
+ storage: function() {
172
+ // Feature detect localStorage + local reference
173
+ var storage, fail, uid;
174
+
175
+ try {
176
+ uid = new Date;
177
+ (storage = window.localStorage).setItem(uid, uid);
178
+ fail = storage.getItem(uid) != uid;
179
+ storage.removeItem(uid);
180
+ fail && (storage = false);
181
+ } catch(e) {}
182
+
183
+ if (storage) {
184
+ console.log('=> localStorage is available');
185
+ return storage;
186
+ } else {
187
+ console.log('=> localStorage is NOT available');
188
+ return null;
189
+ }
190
+ },
191
+ // The web application is receiving a callback from the client to set a value or execute some
192
+ // behavior. If the view has an `onClientCallback` method, this will be executed first. It can
193
+ // cancel any further event handling by returning `true`. Otherwise, the application sends the
194
+ // event to all of the `clientCallbackHandlers` that are registered with the application, in the
195
+ // order they were registered.
196
+ //
197
+ // @param {String} name
198
+ // @param {Object} value
199
+ onClientCallback: function(name, value) {
200
+ var handled = false;
201
+ var view = this.currentView;
202
+ if (view && view.onClientCallback) {
203
+ handled = view.onClientCallback.call(view, name, value);
204
+ }
205
+ if (!handled) {
206
+ this.clientCallbackHandlers.forEach(function(handler) {
207
+ handler.call(this, name, value);
208
+ }, this);
209
+ }
210
+ },
211
+ // load a view from cache or the server when we get a hashchange event
212
+ _onHashChange: function(event, memo) {
213
+ if (!this.params.clientSupportsClose) {
214
+ // If the app is not initializing, and the view is stashed, do not do anything
215
+ if (!this.app_is_initializing && memo.stash) {
216
+ console.log('=> App is not initializing & a view is stashed. RETURN');
217
+ return;
218
+ }
219
+ }
220
+
221
+ $.assert(memo.view, 'The requested URL could not be found. { href: ' + location.href + ' }');
222
+
223
+ var view = this.cache[History.currentHash()];
224
+ if (view) {
225
+ console.log('@@ Getting view from cache.');
226
+ view.params.cached = true;
227
+ this.appendView(view, true);
228
+ } else {
229
+ console.log('@@ Creating view from scratch.');
230
+ this.showView(memo);
231
+ }
232
+
233
+ if (!this.params.clientSupportsClose) {
234
+ this.app_is_initializing = false;
235
+ }
236
+ },
237
+ // Initializes application functionality on page ready, and kicks off loading the view.
238
+ //
239
+ // @private
240
+ _onReady: function() {
241
+ $(document.body)
242
+ .on('hashchange', $.proxy(Application._onHashChange, this))
243
+ .on('timerReachInterval', $.proxy(Application._onTimerReachedInterval, this))
244
+ .list_hl('tr[href]', function(e, el) {
245
+ History.add( $.deserialize( el.attr('href').slice(1) ) );
246
+ });
247
+ }
248
+ });
249
+
250
+ // Singleton.
251
+ console.log('@@@@ Starting Application');
252
+ Application = new Application();
253
+
254
+ Zepto(function($) {
255
+ Application._onReady();
256
+ });
@@ -1,9 +1,10 @@
1
+ /*global View Application unescape */
1
2
  // A basic view implementation that expects an `URL` to be passed as one of the parameters
2
3
  // to the view; it will format that `URL` using any other parameters, and use it to retrieve
3
4
  // an `HTML` fragment from the server. This is only one of many strategies that could be
4
5
  // followed to create views, of course. It also assumes that the parameters object will
5
6
  // get a title property.
6
- var BaseView = View.extend({
7
+ window.BaseView = View.extend({
7
8
  // Subclasses must ensure there is an url property to the parameter object passed
8
9
  // into this superconstructor, i.e.:
9
10
  //
@@ -71,21 +72,21 @@ var BaseView = View.extend({
71
72
  console.log('$$ BaseView.create(): Not appending autoupdate view. We are no longer on that view!');
72
73
  } else {
73
74
  // NBA3 only
74
- if ($.highResifyLogosForRetinaAndAndroid) {
75
- response = $.highResifyLogosForRetinaAndAndroid(response);
76
- }
75
+ // if ($.highResifyLogosForRetinaAndAndroid) {
76
+ // response = $.highResifyLogosForRetinaAndAndroid(response);
77
+ // }
77
78
 
78
79
  // Get a node rather than plain HTML
79
80
  this.element = Application.buffer.html(response).find('div').get(0);
80
81
  Application.appendView(this);
81
82
 
82
83
  // NBA3 only
83
- if ($.delayedLoad) {
84
- $.delayedLoad();
85
- }
84
+ // if ($.delayedLoad) {
85
+ // $.delayedLoad();
86
+ // }
86
87
  }
87
88
  }, this),
88
- error: function(e) {
89
+ error: function() {
89
90
  $.hideLoader();
90
91
  $.flash("A network error occurred.", Application.currentView.params, true);
91
92
  throw new Error('An error occurred.');
@@ -112,8 +113,10 @@ var BaseView = View.extend({
112
113
  var data = JSON.parse(params);
113
114
 
114
115
  for (var key in data) {
115
- // If you escape a non-string value, it is first converted to a string, which we don't want
116
- this.params[key] = (typeof data[key] == "string") ? unescape(data[key]) : data[key];
116
+ if (data.hasOwnProperty(key)) {
117
+ // If you escape a non-string value, it is first converted to a string, which we don't want
118
+ this.params[key] = (typeof data[key] === "string") ? unescape(data[key]) : data[key];
119
+ }
117
120
  }
118
121
  }
119
122
  }
@@ -1,7 +1,7 @@
1
1
  // A very simple history implementation. Generates events on the `document.body` element when the
2
2
  // hash changes, and includes a parsed object of parameters or a string from the hash.
3
3
 
4
- var History = (function() {
4
+ window.History = (function() {
5
5
 
6
6
  var currentHash = null;
7
7
 
@@ -40,6 +40,6 @@ var History = (function() {
40
40
  currentHash: function() {
41
41
  return getHash();
42
42
  }
43
- }
43
+ };
44
44
 
45
45
  })();
@@ -1,7 +1,8 @@
1
+ /*global Class Application Client MOCK_FAVES_CONFIG */
1
2
  //= require core/mock_config.js
2
3
 
3
4
  // Mock client interface
4
- var Mock = Class.extend({
5
+ window.Mock = Class.extend({
5
6
  initialize: function(client) {
6
7
  this.client = client;
7
8
  window.addEventListener("load", $.proxy(this._makeToolbar, this), true);
@@ -10,12 +11,7 @@ var Mock = Class.extend({
10
11
  var toolbar = document.createElement("div");
11
12
  toolbar.id = "mock-toolbar-container";
12
13
  // CSS is set in `mock.css`
13
- toolbar.innerHTML = "<div id='mock-toolbar'>\
14
- <div id='mock-left'></div>\
15
- <div id='mock-center'></div>\
16
- <div id='mock-right'></div>\
17
- </div>";
18
-
14
+ toolbar.innerHTML = "<div id=mock-toolbar><div id=mock-left></div><div id=mock-center></div><div id=mock-right></div></div>";
19
15
  document.body.insertBefore(toolbar, document.body.firstChild);
20
16
  this.toolbar = toolbar;
21
17
  }
@@ -26,7 +22,7 @@ var Mock = new Mock(Client);
26
22
  Client.makeButtons = function(str) {
27
23
  if (!str) return;
28
24
  var specs = str.split(":");
29
- if (specs.length != 2) {
25
+ if (specs.length !== 2) {
30
26
  alert("There should be 2 segments to a buttons specification");
31
27
  } else {
32
28
  makeButtonsForSection(specs[0], "mock-left");
@@ -76,7 +72,9 @@ Client.notify = function(obj) {
76
72
  if (window.console && window.console.log) {
77
73
  var arr = [];
78
74
  for (var prop in obj) {
79
- arr.push(prop + ": " + obj[prop]);
75
+ if (obj.hasOwnProperty(prop)) {
76
+ arr.push(prop + ": " + obj[prop]);
77
+ }
80
78
  }
81
79
  console.log("))) Client.notify({ " + arr.join(", ") + " })");
82
80
  }
@@ -1,5 +1,7 @@
1
+ /*global Class */
2
+
1
3
  // Timer class for use with auto-updating of game status'.
2
- var Timer = Class.extend({
4
+ window.Timer = Class.extend({
3
5
  initialize: function(func, period) {
4
6
  this.func = func;
5
7
  this.period = period;
@@ -1,3 +1,4 @@
1
+ /*global Class */
1
2
  // If your view implements an initialization function, that function will be passed a parameter
2
3
  // object containing all the query parameters in the URL that triggered the view's creation, plus
3
4
  // all of those values that were assigned to the view when it was registered and bound to a name.
@@ -5,7 +6,7 @@
5
6
  // `this.params`). At the end of a view's create method, it should have inserted a dom element into
6
7
  // the body of the page. This will be assigned as the "element" property to the view by the application,
7
8
  // as it needs to be referenced outside your view class.
8
- var View = Class.extend({
9
+ window.View = Class.extend({
9
10
  // This method should create (by any means desirable) a DOM element and insert it into the page.
10
11
  // Our base implementation queries for an HTML fragment from the server, but this is up to
11
12
  // your view implementation.
@@ -21,27 +21,29 @@ String.prototype.format = function(hash, translate_key) {
21
21
  translate_key = translate_key || false;
22
22
  var s = this;
23
23
  for (var key in hash) {
24
- var re = new RegExp('\\{' + (key) + '\\}','gm');
25
- s = s.replace(re, hash[key]);
24
+ if (hash.hasOwnProperty(key)) {
25
+ var re = new RegExp('\\{' + (key) + '\\}','gm');
26
+ s = s.replace(re, hash[key]);
27
+ }
26
28
  }
27
29
  return s;
28
- }
30
+ };
29
31
 
30
32
  window.$id = function(id) {
31
33
  return document.getElementById(id);
32
- }
34
+ };
33
35
 
34
36
  // Capitalize the first character of a string.
35
37
  String.prototype.capitalize = function() {
36
38
  return this.charAt(0).toUpperCase() + this.substr(1).toLowerCase();
37
- }
39
+ };
38
40
 
39
41
  // zepto/querySelector, understandably, can't deal with periods in the id attr.
40
42
  String.prototype.dotToBar = function() {
41
43
  return this.replace(/\./g,"_");
42
44
  };
43
45
 
44
- var macrojungle = function(tmpl) {
46
+ window.macrojungle = function(tmpl) {
45
47
  // This is kind of stupid.
46
48
  // But at least it's only written once.
47
49
  var html = $('#tmp').empty().microjungle(tmpl).html();
@@ -0,0 +1,195 @@
1
+ ;(function($){
2
+
3
+ var getMetricPrefix = function(view) {
4
+ var metric = view.getMetric();
5
+ if (view.params.view === 'team') {
6
+ metric += '/News';
7
+ }
8
+ return metric;
9
+ };
10
+
11
+ $.articles = function(opts) {
12
+ var view = Application.currentView;
13
+ var defaults = {
14
+ 'params': {},
15
+ 'id': null
16
+ };
17
+ opts = $.extend(defaults, opts);
18
+
19
+ var show_article = function(params) {
20
+ if (params.id.substring(0) !== "#") {
21
+ params.id = "#"+params.id;
22
+ }
23
+
24
+ view.params.activeArticle = params.id;
25
+
26
+ $('.summaries,#filter,.article_wrapper,#footer,#team_heading,#team_tabs').hide();
27
+ $(params.id).show();
28
+
29
+ window.scrollTo(0,1);
30
+
31
+ if (Application.params.clientSupportsClose) {
32
+ Client.notify({
33
+ 'buttons': ($('.summary_link').size() < 2) ? 'Close:' : 'Close:NudgeNav',
34
+ 'metric': getMetricPrefix(view) + '/Detail',
35
+ 'refreshAd': true
36
+ });
37
+ } else {
38
+ // Makes the back button to work
39
+ Application.stashView(view.params);
40
+ Client.notify({
41
+ "buttons": view.params.should_have_buttons,
42
+ "metric": getMetricPrefix(view) + "/Detail",
43
+ "title": view.getTitle(),
44
+ 'refreshAd': true
45
+ });
46
+ }
47
+
48
+ };
49
+
50
+ var handlers = function() {
51
+ $('.resizeable').resizeable();
52
+
53
+ $('.summaries').list_hl('.summary_link', function(e, el) {
54
+ view.params.pageYOffset = window.pageYOffset;
55
+ show_article({ 'id': el.attr('for') });
56
+ });
57
+
58
+ $('.share_btn')
59
+ .off('click')
60
+ .on('click', function(e) {
61
+ var $this = $(this);
62
+ var c = {
63
+ 'title': $this.data('title'),
64
+ 'summary': $this.data('summary'),
65
+ 'url': $this.data('url'),
66
+ 'platform': Application.client.ios ? 'iOS' : 'Android',
67
+ 'img': $this.data('fbimg'),
68
+ 'shortName': Application.params.shortName,
69
+ 'longName': Application.params.longName,
70
+ 'sharingUrl': Application.params.sharingUrl
71
+ };
72
+ var email_message_tmpl = [
73
+ ['p',
74
+ 'I found "{title}" and thought you might be interested: '.format(c),
75
+ ['a', {'href': '@SHORTENED_URL@'}, '@SHORTENED_URL@']
76
+ ],
77
+ ['p', '--'],
78
+ ['p', 'Get more from {longName} while on the go. '.format(c),
79
+ ['a', {'href': '{sharingUrl}'.format(c)}, 'Download'],
80
+ ' it now.'
81
+ ]
82
+ ];
83
+ var twitter_message = '"{title}" @SHORTENED_URL@ via {shortName}'.format(c);
84
+ var email_subject = '"{title}" via {longName} for {platform}'.format(c);
85
+
86
+ var share_params = {
87
+ 'action': 'share',
88
+ 'twitter_message': twitter_message,
89
+ 'facebook_name': c.title,
90
+ 'facebook_message': c.summary,
91
+ 'facebook_image': c.img,
92
+ 'email_subject': email_subject,
93
+ 'email_message': macrojungle(email_message_tmpl),
94
+ 'url': c.url
95
+ };
96
+ // Don't send these to iOS. See bug 24061.
97
+ if (Application.client.android) {
98
+ share_params.title = c.title;
99
+ share_params.excerpt = c.summary;
100
+ if (c.img) {
101
+ share_params.img_url = c.img;
102
+ }
103
+ }
104
+ Client.notify(share_params);
105
+ });
106
+ };
107
+ var handle_notifications = function(params) {
108
+ params.notifs = params.notifs || [];
109
+
110
+ // Check if this notif has been viewed before.
111
+ if (params.notifs[params.digest]) return;
112
+
113
+ if ($('#'+params.digest).size() > 0) {
114
+ Client.notify({
115
+ 'metric': getMetricPrefix(view) + '/Notification'
116
+ });
117
+ show_article({ 'id': params.digest });
118
+ } else {
119
+ if (Application.params.clientSupportsAlert) {
120
+ Client.notify({
121
+ 'action': 'alert',
122
+ 'heading': 'An error occurred',
123
+ 'text': 'The requested article could not be found.'
124
+ });
125
+ } else {
126
+ alert('The requested article could not be found.');
127
+ }
128
+ }
129
+ // Mark that this notif has been viewed, or even though
130
+ // the digest was bogus, mark that it was read so
131
+ // the user isn't told the article doesn't exist over and over.
132
+ params.notifs[params.digest] = true;
133
+ };
134
+
135
+ // This is used with the Nudge Nav.
136
+ if (opts.id) {
137
+ show_article({ 'id': opts.id });
138
+ // This is the standard initialization.
139
+ } else {
140
+ $('.article_wrapper').hide();
141
+ $('.summaries,#filter,#footer,#team_heading,#team_tabs').show();
142
+
143
+ handlers();
144
+
145
+ if (opts.params.digest) {
146
+ handle_notifications(opts.params);
147
+ }
148
+ if (!Application.params.clientSupportsClose) {
149
+ Application.currentView.params.should_have_buttons = ($('.summary_link').size() < 2) ? "Back:" : "Back:NudgeNav";
150
+ }
151
+ }
152
+
153
+ };
154
+
155
+ // Supports nudge navigation via client application.
156
+ // Navigation loops.
157
+ $.articlesClientCallback = function(name, value) {
158
+ var view = Application.currentView;
159
+ var params = view.params;
160
+ var $article = $(params.activeArticle);
161
+
162
+ if (name === 'close') {
163
+ $('.article_wrapper').hide();
164
+ $('.summaries,#filter,#footer,#team_heading,#team_tabs').show();
165
+
166
+ window.scrollTo(0, view.params.pageYOffset);
167
+
168
+ Client.notify({
169
+ 'buttons': "Back:",
170
+ 'metric': getMetricPrefix(view),
171
+ 'refreshAd': true
172
+ });
173
+ }
174
+
175
+ if (name === 'nudge') {
176
+ if (value === 'next' && $article.data('next')) {
177
+ $.articles({
178
+ 'params': params,
179
+ 'id': $article.data('next')
180
+ });
181
+ } else if (value === 'prev' && $article.data('prev')) {
182
+ $.articles({
183
+ 'params': params,
184
+ 'id': $article.data('prev')
185
+ });
186
+ } else {
187
+ $.articles({
188
+ 'params': params,
189
+ 'id': $('.article_wrapper').first().attr('id')
190
+ });
191
+ }
192
+ }
193
+ };
194
+
195
+ })(Zepto);
@@ -7,5 +7,5 @@
7
7
  }
8
8
  throw new Error(error);
9
9
  }
10
- }
10
+ };
11
11
  })(Zepto);
@@ -1,9 +1,10 @@
1
+ /*global Application $id */
1
2
  // Behavior for calendar (schedule) navigation.
2
3
  ;(function($){
3
4
  $.calnav = function(){
4
5
  $('.calnav .item[href]')
5
6
  .off('click')
6
- .on('click', function(e) {
7
+ .on('click', function() {
7
8
  Application.showView(
8
9
  $.deserialize(this.getAttribute('href'))
9
10
  );
@@ -1,3 +1,4 @@
1
+ /*global Application */
1
2
  ;(function($){
2
3
  $.fn.collapsible = function() {
3
4