sports_db 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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