Merge lp:~rharding/launchpad/911857_extract_js into lp:launchpad

Proposed by Richard Harding
Status: Merged
Approved by: Richard Harding
Approved revision: no longer in the source branch.
Merged at revision: 14891
Proposed branch: lp:~rharding/launchpad/911857_extract_js
Merge into: lp:launchpad
Diff against target: 742 lines (+435/-287)
2 files modified
lib/lp/bugs/javascript/buglisting.js (+428/-218)
lib/lp/bugs/templates/bugtask-macros-tableview.pt (+7/-69)
To merge this branch: bzr merge lp:~rharding/launchpad/911857_extract_js
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+95438@code.launchpad.net

Commit message

[r=jcsackett][bug=911857] Move the JS out of the bugtask-macros-tableview and into a new JS TableView object

Description of the change

= Summary =
There is a lot of JS code in the bugtask-macros-tableview.pt that makes it difficult to test and find/replace when updating and fixing things since it's a .pt file and not a .js file.

Templates should have bare bones JS code in them.

== Proposed Fix ==
This pulls out the JS into a buglisting.TableView object that handles building and binding up the view that the template was previously doing.

It tries to break up the building/binding/rendering into a more 'widget' like setup. This is somewhat how the JS would be in a YUI 3.5 View object. So it's partially setup to work in that format.

== Tests ==

== Demo and Q/A ==
The buglisting page shouldn't appear any differently. Head to launchpad/+bugs url and using the buglisting UI. The url should still update due to history events, ajax calls made out due to sort order changes, and the gear icon should enable the user to customize the view as usual.

== Lint ==

Linting changed files:
  lib/lp/bugs/javascript/buglisting.js
  lib/lp/bugs/templates/bugtask-macros-tableview.pt

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

This looks good to me. Thanks for removing all that code from the template.

review: Approve
Revision history for this message
pedro cavazos (aaacavazos) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/javascript/buglisting.js'
2--- lib/lp/bugs/javascript/buglisting.js 2012-01-10 21:25:39 +0000
3+++ lib/lp/bugs/javascript/buglisting.js 2012-03-01 19:32:21 +0000
4@@ -7,222 +7,432 @@
5 * @submodule buglisting
6 */
7
8-YUI.add('lp.bugs.buglisting', function(Y) {
9-
10-var module = Y.namespace('lp.bugs.buglisting');
11-
12-
13-/**
14- * Constructor.
15- *
16- * This is the model of the current batch, including the ordering, position,
17- * and what fields are visibile.
18- *
19- * These values are stored in the History object, so that the browser
20- * back/next buttons correctly adjust. The system defaults for field
21- * visibility are fixed, so they are stored directly on the object.
22- *
23- * Accepts a config containing:
24- * - field_visibility the requested field visibility as an associative array
25- * - field_visibility_defaults the system defaults for field visibility as an
26- * associative array.
27- * - batch_key: A string representing the position and ordering of the
28- * current batch, as returned by listing_navigator.get_batch_key
29- */
30-module.BugListingModel = function() {
31- module.BugListingModel.superclass.constructor.apply(this, arguments);
32-};
33-
34-
35-module.BugListingModel.NAME = 'buglisting-model';
36-
37-
38-module.BugListingModel.ATTRS = {
39- field_visibility_defaults: {
40- value: null
41- }
42-};
43-
44-
45-Y.extend(module.BugListingModel, Y.Base, {
46- /**
47- * Initializer sets up the History object that stores most of the model
48- * data.
49- */
50- initializer: function(config) {
51- this.set('history', new Y.History({
52- initialState: Y.merge(
53- config.field_visibility, {batch_key: config.batch_key})
54- }));
55- },
56-
57- /**
58- * Return the current field visibility, as an associative array.
59- * Since the history contains field values that are not field-visibility,
60- * use field_visibility_defaults to filter out non-field-visibility
61- * values.
62- */
63- get_field_visibility: function() {
64- var result = this.get('history').get();
65- var key_source = this.get('field_visibility_defaults');
66- Y.each(result, function(value, key) {
67- if (!key_source.hasOwnProperty(key)){
68- delete result[key];
69- }
70- });
71- return result;
72- },
73-
74- /**
75- * Set the field visibility, updating history. Accepts an associative
76- * array.
77- */
78- set_field_visibility: function(value) {
79- this.get('history').add(value);
80- },
81-
82- /**
83- * Return the current batch key.
84- */
85- get_batch_key: function() {
86- return this.get('history').get('batch_key');
87- },
88-
89- /**
90- * Set the current batch. The batch_key and the query mapping identifying
91- * the batch must be supplied.
92- */
93- set_batch: function(batch_key, query) {
94- var url = '?' + Y.QueryString.stringify(query);
95- this.get('history').addValue('batch_key', batch_key, {url: url});
96- }
97-});
98-
99-
100-/**
101- * Constructor.
102- * current_url is used to determine search params.
103- * cache is the JSONRequestCache for the batch.
104- * template is the template to use for rendering batches.
105- * target is a YUI node to update when rendering batches.
106- * navigation_indices is a YUI NodeList of nodes to update with the current
107- * batch info.
108- * io_provider is something providing the Y.io interface, typically used for
109- * testing. Defaults to Y.io.
110- */
111-module.BugListingNavigator = function(config) {
112- module.BugListingNavigator.superclass.constructor.apply(
113- this, arguments);
114-};
115-
116-module.BugListingNavigator.ATTRS = {
117-};
118-
119-Y.extend(
120- module.BugListingNavigator,
121- Y.lp.app.listing_navigator.ListingNavigator, {
122- _bindUI: function () {
123- Y.lp.app.inlinehelp.init_help();
124- },
125-
126- initializer: function(config) {
127- this.get('model').get('history').after(
128- 'change', this.history_changed, this);
129- },
130- /**
131- * Event handler for history:change events.
132- */
133- history_changed: function(e) {
134- if (e.newVal.hasOwnProperty('batch_key')) {
135- var batch_key = e.newVal.batch_key;
136- var batch = this.get('batches')[batch_key];
137- this.pre_fetch_batches();
138- this.render();
139- this._bindUI();
140- }
141- else {
142- // Handle Chrom(e|ium)'s initial popstate.
143- this.get('model').get('history').replace(e.prevVal);
144- }
145- },
146-
147- /**
148- * Return the model to use for rendering the batch. This will include
149- * updates to field visibility.
150- */
151- get_render_model: function(current_batch) {
152- return Y.merge(
153- current_batch.mustache_model,
154- this.get('model').get_field_visibility());
155- },
156-
157- /**
158- * Handle a previously-unseen batch by storing it in the cache and
159- * stripping out field_visibility values that would otherwise shadow the
160- * real values.
161- */
162- handle_new_batch: function(batch) {
163- var key, i;
164- Y.each(batch.field_visibility, function(value, key) {
165- for (i = 0; i < batch.mustache_model.items.length; i++) {
166- delete batch.mustache_model.items[i][key];
167- }
168- });
169- return this.constructor.superclass.handle_new_batch.call(this, batch);
170- }
171-
172-},{
173- make_model: function(batch_key, cache) {
174- return new module.BugListingModel({
175- batch_key: batch_key,
176- field_visibility: cache.field_visibility,
177- field_visibility_defaults: cache.field_visibility_defaults
178- });
179- },
180- get_search_params: function(config) {
181- var search_params = Y.lp.app.listing_navigator.get_query(
182- config.current_url);
183- delete search_params.start;
184- delete search_params.memo;
185- delete search_params.direction;
186- delete search_params.orderby;
187- return search_params;
188- }
189-});
190-
191-/**
192- * Factory to return a BugListingNavigator for the given page.
193- */
194-module.BugListingNavigator.from_page = function() {
195- var target = Y.one('#client-listing');
196- if (Y.Lang.isNull(target)){
197- return null;
198- }
199- var navigation_indices = Y.all('.batch-navigation-index');
200- var pre_fetch = Y.lp.app.listing_navigator.get_feature_flag(
201- 'bugs.dynamic_bug_listings.pre_fetch');
202- Y.lp.app.listing_navigator.linkify_navigation();
203- var navigator = new module.BugListingNavigator({
204- current_url: window.location,
205- cache: LP.cache,
206- template: LP.mustache_listings,
207- target: target,
208- navigation_indices: navigation_indices,
209- pre_fetch: Boolean(pre_fetch)
210- });
211- navigator.set('backwards_navigation', Y.all('.first,.previous'));
212- navigator.set('forwards_navigation', Y.all('.last,.next'));
213- navigator.clickAction('.first', navigator.first_batch);
214- navigator.clickAction('.next', navigator.next_batch);
215- navigator.clickAction('.previous', navigator.prev_batch);
216- navigator.clickAction('.last', navigator.last_batch);
217- navigator.render_navigation();
218- return navigator;
219-};
220-
221-
222-
223-}, "0.1", {
224- "requires": [
225- "history", "node", 'lp.app.listing_navigator', 'lp.app.inlinehelp', 'lp.app.indicator']
226+YUI.add('lp.bugs.buglisting', function (Y) {
227+
228+ var module = Y.namespace('lp.bugs.buglisting');
229+
230+
231+ /**
232+ * Constructor.
233+ *
234+ * This is the model of the current batch, including the ordering,
235+ * position, and what fields are visibile.
236+ *
237+ * These values are stored in the History object, so that the browser
238+ * back/next buttons correctly adjust. The system defaults for field
239+ * visibility are fixed, so they are stored directly on the object.
240+ *
241+ * Accepts a config containing:
242+ * - field_visibility the requested field visibility as an associative
243+ * array
244+ * - field_visibility_defaults the system defaults for field visibility
245+ * as an associative array.
246+ * - batch_key: A string representing the position and ordering of the
247+ * current batch, as returned by listing_navigator.get_batch_key
248+ */
249+ module.BugListingModel = function () {
250+ module.BugListingModel.superclass.constructor.apply(this, arguments);
251+ };
252+
253+
254+ module.BugListingModel.NAME = 'buglisting-model';
255+
256+
257+ module.BugListingModel.ATTRS = {
258+ field_visibility_defaults: {
259+ value: null
260+ }
261+ };
262+
263+
264+ Y.extend(module.BugListingModel, Y.Base, {
265+ /**
266+ * Initializer sets up the History object that stores most of the
267+ * model data.
268+ */
269+ initializer: function(config) {
270+ this.set('history', new Y.History({
271+ initialState: Y.merge(
272+ config.field_visibility, {
273+ batch_key: config.batch_key
274+ }
275+ )
276+ }));
277+ },
278+
279+ /**
280+ * Return the current field visibility, as an associative array.
281+ * Since the history contains field values that are not
282+ * field-visibility, use field_visibility_defaults to filter out
283+ * non-field-visibility values.
284+ */
285+ get_field_visibility: function () {
286+ var result = this.get('history').get();
287+ var key_source = this.get('field_visibility_defaults');
288+ Y.each(result, function(value, key) {
289+ if (!key_source.hasOwnProperty(key)){
290+ delete result[key];
291+ }
292+ });
293+ return result;
294+ },
295+
296+ /**
297+ * Set the field visibility, updating history. Accepts an associative
298+ * array.
299+ */
300+ set_field_visibility: function(value) {
301+ this.get('history').add(value);
302+ },
303+
304+ /**
305+ * Return the current batch key.
306+ */
307+ get_batch_key: function() {
308+ return this.get('history').get('batch_key');
309+ },
310+
311+ /**
312+ * Set the current batch. The batch_key and the query mapping
313+ * identifying the batch must be supplied.
314+ */
315+ set_batch: function(batch_key, query) {
316+ var url = '?' + Y.QueryString.stringify(query);
317+ this.get('history').addValue('batch_key', batch_key, {url: url});
318+ }
319+ });
320+
321+
322+ /**
323+ * Constructor.
324+ * current_url is used to determine search params.
325+ * cache is the JSONRequestCache for the batch.
326+ * template is the template to use for rendering batches.
327+ * target is a YUI node to update when rendering batches.
328+ * navigation_indices is a YUI NodeList of nodes to update with the
329+ * current batch info.
330+ * io_provider is something providing the Y.io interface, typically used
331+ * for testing. Defaults to Y.io.
332+ */
333+ module.BugListingNavigator = function(config) {
334+ module.BugListingNavigator.superclass.constructor.apply(
335+ this, arguments);
336+ };
337+
338+ module.BugListingNavigator.ATTRS = {
339+ };
340+
341+ Y.extend(
342+ module.BugListingNavigator,
343+ Y.lp.app.listing_navigator.ListingNavigator, {
344+ _bindUI: function () {
345+ Y.lp.app.inlinehelp.init_help();
346+ },
347+
348+ initializer: function(config) {
349+ this.get('model').get('history').after(
350+ 'change', this.history_changed, this);
351+ },
352+ /**
353+ * Event handler for history:change events.
354+ */
355+ history_changed: function(e) {
356+ if (e.newVal.hasOwnProperty('batch_key')) {
357+ var batch_key = e.newVal.batch_key;
358+ var batch = this.get('batches')[batch_key];
359+ this.pre_fetch_batches();
360+ this.render();
361+ this._bindUI();
362+ }
363+ else {
364+ // Handle Chrom(e|ium)'s initial popstate.
365+ this.get('model').get('history').replace(e.prevVal);
366+ }
367+ },
368+
369+ /**
370+ * Return the model to use for rendering the batch. This will include
371+ * updates to field visibility.
372+ */
373+ get_render_model: function(current_batch) {
374+ return Y.merge(
375+ current_batch.mustache_model,
376+ this.get('model').get_field_visibility());
377+ },
378+
379+ /**
380+ * Handle a previously-unseen batch by storing it in the cache and
381+ * stripping out field_visibility values that would otherwise shadow the
382+ * real values.
383+ */
384+ handle_new_batch: function(batch) {
385+ var key, i;
386+ Y.each(batch.field_visibility, function(value, key) {
387+ for (i = 0; i < batch.mustache_model.items.length; i++) {
388+ delete batch.mustache_model.items[i][key];
389+ }
390+ });
391+ return this.constructor.superclass.handle_new_batch.call(this,
392+ batch);
393+ }
394+
395+ },{
396+ make_model: function(batch_key, cache) {
397+ return new module.BugListingModel({
398+ batch_key: batch_key,
399+ field_visibility: cache.field_visibility,
400+ field_visibility_defaults: cache.field_visibility_defaults
401+ });
402+ },
403+ get_search_params: function(config) {
404+ var search_params = Y.lp.app.listing_navigator.get_query(
405+ config.current_url);
406+ delete search_params.start;
407+ delete search_params.memo;
408+ delete search_params.direction;
409+ delete search_params.orderby;
410+ return search_params;
411+ }
412+ });
413+
414+ /**
415+ * Factory to return a BugListingNavigator for the given page.
416+ */
417+ module.BugListingNavigator.from_page = function() {
418+ var target = Y.one('#client-listing');
419+ if (Y.Lang.isNull(target)){
420+ return null;
421+ }
422+ var navigation_indices = Y.all('.batch-navigation-index');
423+ var pre_fetch = Y.lp.app.listing_navigator.get_feature_flag(
424+ 'bugs.dynamic_bug_listings.pre_fetch');
425+ Y.lp.app.listing_navigator.linkify_navigation();
426+ var navigator = new module.BugListingNavigator({
427+ current_url: window.location,
428+ cache: LP.cache,
429+ template: LP.mustache_listings,
430+ target: target,
431+ navigation_indices: navigation_indices,
432+ pre_fetch: Boolean(pre_fetch)
433+ });
434+ navigator.set('backwards_navigation', Y.all('.first,.previous'));
435+ navigator.set('forwards_navigation', Y.all('.last,.next'));
436+ navigator.clickAction('.first', navigator.first_batch);
437+ navigator.clickAction('.next', navigator.next_batch);
438+ navigator.clickAction('.previous', navigator.prev_batch);
439+ navigator.clickAction('.last', navigator.last_batch);
440+ navigator.render_navigation();
441+ return navigator;
442+ };
443+
444+
445+ /**
446+ * Helper view object for managing the buglisting code on the actual table
447+ * view.
448+ *
449+ * @class TableView
450+ * @extends Y.Base
451+ * @namespace lp.bugs.buglisting
452+ *
453+ */
454+ module.TableView = Y.Base.create('buglisting-tableview', Y.Base,
455+ [], {
456+
457+ /**
458+ * Hook into the model events to aid in setting up history events.
459+ *
460+ * @method _bind_history
461+ * @private
462+ *
463+ */
464+ _bind_history: function () {
465+ var that = this;
466+ this.navigator.get('model').get('history').after(
467+ 'change', function(e) {
468+ // Only update the sort buttons if we've got a valid batch
469+ // key.
470+ if (Y.Object.hasKey(e.newVal, 'batch_key')) {
471+ Y.lp.buglisting_utils.update_sort_button_visibility(
472+ that.orderby,
473+ e.newVal
474+ );
475+ }
476+ }
477+ );
478+ },
479+
480+ /**
481+ * Setup the order bar widget for use in the table view.
482+ *
483+ * @method _build_orderbar
484+ * @private
485+ *
486+ */
487+ _build_orderbar: function () {
488+ var that = this;
489+ that.orderby = new Y.lp.ordering.OrderByBar({
490+ srcNode: Y.one('#bugs-orderby'),
491+ sort_keys: this.get('sort_keys'),
492+ active: this.get('active_sort_key'),
493+ sort_order: this.get('sort_order'),
494+ config_slot: true
495+ });
496+ Y.on('orderbybar:sort', function(e) {
497+ that.navigator.first_batch(e);
498+ });
499+ },
500+
501+ /**
502+ * We need to parse out the active key in case it indicates we should be
503+ * desc sorting, etc.
504+ *
505+ * @method _init_sort
506+ * @private
507+ *
508+ */
509+ _init_sort: function () {
510+ var active_key = this.get('active_sort_key');
511+ if (active_key.charAt(0) === '-') {
512+ this.set('active_sort_key',
513+ active_key.substring(1, active_key.length));
514+ this.set('sort_order', 'desc');
515+ }
516+ },
517+
518+ /**
519+ * If after init we still don't have any keys to sort on, we go with our
520+ * default sort key.
521+ *
522+ * @method _check_default_sort
523+ * @private
524+ *
525+ */
526+ _check_default_sort: function () {
527+ var active_key = this.get('active_key');
528+ var unknown_sort_key = true;
529+
530+ Y.each(this.get('sort_keys'), function(sort_key) {
531+ if (sort_key[0] === active_key) {
532+ unknown_sort_key = false;
533+ }
534+ });
535+ if (unknown_sort_key) {
536+ this.set('active_sort_key', this.get('default_sort'));
537+ }
538+ },
539+
540+ /**
541+ * General YUI initializer setting up the tableview.
542+ *
543+ * @method intializer
544+ * @param {Object} cfg
545+ *
546+ */
547+ initializer: function (cfg) {
548+ this.navigator =
549+ Y.lp.bugs.buglisting.BugListingNavigator.from_page();
550+
551+ if (Y.Lang.isNull(this.navigator)){
552+ return;
553+ }
554+
555+ // now that we've set the values from the LP.cache, let's process it
556+ this._init_sort();
557+ // if we don't have sort values, we might want to set some defaults
558+ this._check_default_sort();
559+ this._build_orderbar();
560+ this._bind_history();
561+ },
562+
563+ /**
564+ * Handle any UI binding, building for the tableview.
565+ *
566+ * @method render
567+ *
568+ */
569+ render: function () {
570+ var that = this;
571+ var field_visibility =
572+ that.navigator.get('model').get_field_visibility();
573+
574+ that.orderby.always_display = ['title'];
575+ that.orderby.render();
576+
577+ // The listing util needs to be called AFTER the orderby widget is
578+ // rendered or the little gear icon has no home and ends up in DOM
579+ // limbo land.
580+ var config_node = that.orderby.get('config_node');
581+ that.list_util = new Y.lp.buglisting_utils.BugListingConfigUtil({
582+ srcNode: config_node,
583+ model: that.navigator.get('model')
584+ });
585+ that.list_util.render();
586+
587+ Y.on('buglisting-config-util:fields-changed', function(e) {
588+ that.navigator.change_fields(
589+ that.list_util.get('field_visibility'));
590+ });
591+
592+ // The advanced search page contains sort options that have
593+ // no related data fields we can display. If a user has selected
594+ // such a sort order, this sort option should always be visible.
595+ var check_visibility =
596+ field_visibility['show_' + this.get('active_sort_key')];
597+ if (check_visibility === undefined) {
598+ that.orderby.always_display.push(active_sort_key);
599+ }
600+
601+ Y.lp.buglisting_utils.update_sort_button_visibility(
602+ that.orderby,
603+ field_visibility
604+ );
605+ }
606+ }, {
607+ ATTRS: {
608+ /**
609+ * @attribute default_sort
610+ * @default importance
611+ * @type String
612+ *
613+ */
614+ default_sort: {
615+ value: 'importance'
616+ },
617+
618+ /**
619+ * @attribute active_sort_key
620+ * @default undefined
621+ * @type string
622+ *
623+ */
624+ active_sort_key: {
625+
626+ },
627+
628+ /**
629+ * @attribute sort_keys
630+ * @default undefined
631+ * @type Array
632+ *
633+ */
634+ sort_keys: {
635+ },
636+
637+ /**
638+ * @attribute sort_order
639+ * @default asc
640+ * @type String
641+ *
642+ */
643+ sort_order: {
644+ value: 'asc'
645+ }
646+ }
647+ });
648+
649+}, '0.1', {
650+ 'requires': [
651+ 'history', 'node', 'lp.app.listing_navigator', 'lp.app.inlinehelp',
652+ 'lp.app.indicator', 'lp.ordering', 'lp.buglisting_utils'
653+ ]
654 });
655
656=== modified file 'lib/lp/bugs/templates/bugtask-macros-tableview.pt'
657--- lib/lp/bugs/templates/bugtask-macros-tableview.pt 2012-02-07 10:43:49 +0000
658+++ lib/lp/bugs/templates/bugtask-macros-tableview.pt 2012-03-01 19:32:21 +0000
659@@ -681,76 +681,14 @@
660 show_new_listings request/features/bugs.dynamic_bug_listings.enabled;
661 advanced_search view/shouldShowAdvancedForm"
662 tal:condition="python: show_new_listings and not advanced_search">
663- LPJS.use('lp.bugs.buglisting', 'lp.ordering', 'lp.buglisting_utils',
664- function(Y) {
665+ LPJS.use('lp.bugs.buglisting', function(Y) {
666 Y.on('domready', function() {
667- var navigator = Y.lp.bugs.buglisting.BugListingNavigator
668- .from_page();
669- if (Y.Lang.isNull(navigator)){
670- return;
671- }
672- var sort_keys = LP.cache.sort_keys;
673- var active_sort_key = LP.cache.order_by;
674- var sort_order = 'asc';
675- if (active_sort_key.charAt(0) === '-') {
676- active_sort_key = active_sort_key.substring(
677- 1, active_sort_key.length);
678- sort_order = 'desc';
679- }
680- var unknown_sort_key = true;
681- Y.each(sort_keys, function(sort_key) {
682- if (sort_key[0] === active_sort_key) {
683- unknown_sort_key = false;
684- }
685- });
686- if (unknown_sort_key) {
687- active_sort_key = 'importance';
688- }
689- var orderby = new Y.lp.ordering.OrderByBar({
690- srcNode: Y.one('#bugs-orderby'),
691- sort_keys: sort_keys,
692- active: active_sort_key,
693- sort_order: sort_order,
694- config_slot: true
695- });
696- orderby.render();
697- Y.on('orderbybar:sort', function(e) {
698- navigator.first_batch(e);
699- });
700- var model = navigator.get('model');
701- model.get('history').after(
702- 'change', function(e) {
703- // Only update the sort buttons if we've got a valid batch
704- // key.
705- if (Y.Object.hasKey(e.newVal, 'batch_key')) {
706- Y.lp.buglisting_utils.update_sort_button_visibility(
707- orderby,
708- e.newVal
709- );
710- }
711- }
712- );
713- var config_node = orderby.get('config_node');
714- var list_util = new Y.lp.buglisting_utils.BugListingConfigUtil({
715- srcNode: config_node,
716- model: model
717- });
718- list_util.render();
719- Y.on('buglisting-config-util:fields-changed', function(e) {
720- navigator.change_fields(list_util.get('field_visibility'));
721- });
722- var field_visibility =
723- navigator.get('model').get_field_visibility();
724- orderby.always_display = ['title'];
725- // The advanced search page contains sort options that have
726- // no related data fields we can display. If a user has selected
727- // such a sort order, this sort option should always be visible.
728- if (field_visibility['show_' + active_sort_key] === undefined) {
729- orderby.always_display.push(active_sort_key);
730- }
731- Y.lp.buglisting_utils.update_sort_button_visibility(
732- orderby, field_visibility);
733- });
734+ var view = new Y.lp.bugs.buglisting.TableView({
735+ active_sort_key: LP.cache.order_by,
736+ sort_keys: LP.cache.sort_keys
737+ });
738+ view.render();
739+ })
740 });
741 </script>
742 </metal:listing_navigator>