From b3271a42cb174af5c86bd4e6c32bdcb8cf0c6cab Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Nov 2017 16:02:07 -0500 Subject: [PATCH 1/9] Optimize history collection response. Introduce a new collection view type (between view="collection" and view="element") called "element-reference" that fetches the element structure of a collection and enough information to resolve references for leaf datasets but doesn't include details that aren't used in rendering the structure of the collection in the GUI. Prevents transmitting metadata, dataset name (since element identifier is used), info, peek, dbkey, etc.... Backbone and the rest of the infrastructure seems to just work and fills in all remaining details when datasets are actually expanded by users. Optimization mentioned in #3795 - mitigates the issue (hopefully to large degree - but doesn't fix it). --- .../mvc/history/history-content-model.js | 5 +++ lib/galaxy/managers/collections_util.py | 36 ++++++++++++++++++- .../webapps/galaxy/api/history_contents.py | 10 +++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/client/galaxy/scripts/mvc/history/history-content-model.js b/client/galaxy/scripts/mvc/history/history-content-model.js index 326a2ff2aaf..43a85b60da1 100644 --- a/client/galaxy/scripts/mvc/history/history-content-model.js +++ b/client/galaxy/scripts/mvc/history/history-content-model.js @@ -58,6 +58,11 @@ var HistoryContentMixin = { var historyId = this.get("history_id"); var historyContentId = this.get("id"); var url = `${this.urlRoot}${historyId}/contents/${historyContentType}s/${historyContentId}`; + if ( historyContentType == "dataset_collection" ) { + // Don't fetch whole collection - just enought to render outline. Backbone will + // make a detailed request if any datasets are expanded beyond that point. + url = `${url}?view=element-reference`; + } return url; }, diff --git a/lib/galaxy/managers/collections_util.py b/lib/galaxy/managers/collections_util.py index 65101597d77..d8be42cbde3 100644 --- a/lib/galaxy/managers/collections_util.py +++ b/lib/galaxy/managers/collections_util.py @@ -102,7 +102,8 @@ def get_collection_elements(collection, name=""): def dictify_dataset_collection_instance(dataset_collection_instance, parent, security, view="element"): - dict_value = dataset_collection_instance.to_dict(view=view) + hdca_view = "element" if view in ["element", "element-reference"] else "collection" + dict_value = dataset_collection_instance.to_dict(view=hdca_view) encoded_id = security.encode_id(dataset_collection_instance.id) if isinstance(parent, model.History): encoded_history_id = security.encode_id(parent.id) @@ -116,10 +117,43 @@ def dictify_dataset_collection_instance(dataset_collection_instance, parent, sec collection = dataset_collection_instance.collection dict_value['elements'] = [dictify_element(_) for _ in collection.elements] dict_value['populated'] = collection.populated + elif view == "element-reference": + collection = dataset_collection_instance.collection + dict_value['elements'] = [dictify_element_reference(_) for _ in collection.elements] + security.encode_all_ids(dict_value, recursive=True) # TODO: Use Kyle's recursive formulation of this. return dict_value +def dictify_element_reference(element): + """Load minimal details of elements required to show outline of contents in history panel. + + History panel can use this reference to expand to full details if individual dataset elements + are clicked. + """ + dictified = element.to_dict(view="element") + element_object = element.element_object + if element_object is not None: + object_detials = dict( + id=element_object.id, + model_class=element_object.__class__.__name__, + ) + if element.child_collection: + # Recursively yield elements for each nested collection... + child_collection = element.child_collection + object_detials["elements"] = [dictify_element_reference(_) for _ in child_collection.elements] + else: + object_detials["state"] = element_object.state + object_detials["hda_ldda"] = 'hda' + object_detials["history_id"] = element_object.history_id + + else: + object_detials = None + + dictified["object"] = object_detials + return dictified + + def dictify_element(element): dictified = element.to_dict(view="element") element_object = element.element_object diff --git a/lib/galaxy/webapps/galaxy/api/history_contents.py b/lib/galaxy/webapps/galaxy/api/history_contents.py index 357be202da3..6f954009826 100644 --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -145,6 +145,13 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary :param id: 'dataset' or 'dataset_collection' :type history_id: str :param history_id: encoded id string of the HDA's or HDCA's History + :type view: str + :param view: view style of the dataset collection to produce, + 'collection' returns no element information, 'element' + returns detailed element information for all datasets, + 'element-reference' returns a minimal set of information + about datasets (for instance id, type, and state but not + metadata, peek, info, or name). :rtype: dict :returns: dictionary containing detailed HDA or HDCA information @@ -242,7 +249,8 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary def __show_dataset_collection(self, trans, id, history_id, **kwd): dataset_collection_instance = self.__get_accessible_collection(trans, id, history_id) - return self.__collection_dict(trans, dataset_collection_instance, view="element") + view = kwd.get("view", "element") + return self.__collection_dict(trans, dataset_collection_instance, view=view) def __get_accessible_collection(self, trans, id, history_id): return trans.app.dataset_collections_service.get_dataset_collection_instance( From 09c0d2384dbd047a96910f5e6e2daa375b1baf88 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Nov 2017 22:01:08 -0500 Subject: [PATCH 2/9] Limit the number of elements the UX renders/fetches for collections. Fixes #3795 - more or less. --- .../mvc/history/history-content-model.js | 4 +- lib/galaxy/managers/collections_util.py | 122 ++++++++++++++++-- .../webapps/galaxy/api/history_contents.py | 28 +++- 3 files changed, 135 insertions(+), 19 deletions(-) diff --git a/client/galaxy/scripts/mvc/history/history-content-model.js b/client/galaxy/scripts/mvc/history/history-content-model.js index 43a85b60da1..5bf34a9b2c3 100644 --- a/client/galaxy/scripts/mvc/history/history-content-model.js +++ b/client/galaxy/scripts/mvc/history/history-content-model.js @@ -58,10 +58,10 @@ var HistoryContentMixin = { var historyId = this.get("history_id"); var historyContentId = this.get("id"); var url = `${this.urlRoot}${historyId}/contents/${historyContentType}s/${historyContentId}`; - if ( historyContentType == "dataset_collection" ) { + if (historyContentType == "dataset_collection") { // Don't fetch whole collection - just enought to render outline. Backbone will // make a detailed request if any datasets are expanded beyond that point. - url = `${url}?view=element-reference`; + url = `${url}?view=element-reference&fuzzy_count=1000`; } return url; }, diff --git a/lib/galaxy/managers/collections_util.py b/lib/galaxy/managers/collections_util.py index d8be42cbde3..a8233df6eb1 100644 --- a/lib/galaxy/managers/collections_util.py +++ b/lib/galaxy/managers/collections_util.py @@ -1,4 +1,5 @@ import logging +import math from galaxy import exceptions, model, web from galaxy.util import string_as_bool @@ -101,7 +102,7 @@ def get_collection_elements(collection, name=""): return names, hdas -def dictify_dataset_collection_instance(dataset_collection_instance, parent, security, view="element"): +def dictify_dataset_collection_instance(dataset_collection_instance, parent, security, view="element", fuzzy_count=None): hdca_view = "element" if view in ["element", "element-reference"] else "collection" dict_value = dataset_collection_instance.to_dict(view=hdca_view) encoded_id = security.encode_id(dataset_collection_instance.id) @@ -113,19 +114,23 @@ def dictify_dataset_collection_instance(dataset_collection_instance, parent, sec encoded_folder_id = security.encode_id(parent.id) # TODO: Work in progress - this end-point is not right yet... dict_value['url'] = web.url_for('library_content', library_id=encoded_library_id, id=encoded_id, folder_id=encoded_folder_id) - if view == "element": + + if view in ["element", "element-reference"]: collection = dataset_collection_instance.collection - dict_value['elements'] = [dictify_element(_) for _ in collection.elements] - dict_value['populated'] = collection.populated - elif view == "element-reference": - collection = dataset_collection_instance.collection - dict_value['elements'] = [dictify_element_reference(_) for _ in collection.elements] + rank_fuzzy_counts = gen_rank_fuzzy_counts(collection.collection_type, fuzzy_count) + elements, rest_fuzzy_counts = get_fuzzy_count_elements(collection, rank_fuzzy_counts) + if view == "element": + dict_value['populated'] = collection.populated + element_func = dictify_element + else: + element_func = dictify_element_reference + dict_value['elements'] = [element_func(_, rank_fuzzy_counts=rest_fuzzy_counts) for _ in elements] security.encode_all_ids(dict_value, recursive=True) # TODO: Use Kyle's recursive formulation of this. return dict_value -def dictify_element_reference(element): +def dictify_element_reference(element, rank_fuzzy_counts=None): """Load minimal details of elements required to show outline of contents in history panel. History panel can use this reference to expand to full details if individual dataset elements @@ -139,9 +144,12 @@ def dictify_element_reference(element): model_class=element_object.__class__.__name__, ) if element.child_collection: - # Recursively yield elements for each nested collection... + object_detials["collection_type"] = element_object.collection_type child_collection = element.child_collection - object_detials["elements"] = [dictify_element_reference(_) for _ in child_collection.elements] + elements, rest_fuzzy_counts = get_fuzzy_count_elements(child_collection, rank_fuzzy_counts) + # Recursively yield elements for each nested collection... + object_detials["elements"] = [dictify_element_reference(_, rank_fuzzy_counts=rest_fuzzy_counts) for _ in elements] + object_detials["element_count"] = child_collection.element_count else: object_detials["state"] = element_object.state object_detials["hda_ldda"] = 'hda' @@ -154,15 +162,18 @@ def dictify_element_reference(element): return dictified -def dictify_element(element): +def dictify_element(element, rank_fuzzy_counts=None): dictified = element.to_dict(view="element") element_object = element.element_object if element_object is not None: object_detials = element.element_object.to_dict() if element.child_collection: + child_collection = element.child_collection + elements, rest_fuzzy_counts = get_fuzzy_count_elements(child_collection, rank_fuzzy_counts) + # Recursively yield elements for each nested collection... child_collection = element.child_collection - object_detials["elements"] = [dictify_element(_) for _ in child_collection.elements] + object_detials["elements"] = [dictify_element(_, rank_fuzzy_counts=rest_fuzzy_counts) for _ in elements] object_detials["populated"] = child_collection.populated object_detials["element_count"] = child_collection.element_count else: @@ -172,4 +183,91 @@ def dictify_element(element): return dictified +def get_fuzzy_count_elements(collection, rank_fuzzy_counts): + if rank_fuzzy_counts and rank_fuzzy_counts[0]: + rank_fuzzy_count = rank_fuzzy_counts[0] + elements = collection.elements[0:rank_fuzzy_count] + else: + elements = collection.elements + + if rank_fuzzy_counts is not None: + rest_fuzzy_counts = rank_fuzzy_counts[1:] + else: + rest_fuzzy_counts = None + + return elements, rest_fuzzy_counts + + +def gen_rank_fuzzy_counts(collection_type, fuzzy_count=None): + """Turn a global estimate on elements to return to per nested level based on collection type. + + This takes an arbitrary constant and generates an arbitrary constant and is quite messy. + None of this should be relied on as a stable API - it is more of a general guideline to + restrict within broad ranges the amount of objects returned. + + >>> def is_around(x, y): + ... return y - 1 < x and y + 1 > y + ... + >>> gen_rank_fuzzy_counts("list", None) + [None] + >>> gen_rank_fuzzy_counts("list", 500) + [500] + >>> gen_rank_fuzzy_counts("paired", 500) + [2] + >>> gen_rank_fuzzy_counts("list:paired", None) + [None, None] + >>> gen_rank_fuzzy_counts("list:list", 101) # 100 would be edge case at 10 so bump to ensure 11 + [11, 11] + >>> ll, pl = gen_rank_fuzzy_counts("list:paired", 100) + >>> pl + 2 + >>> is_around(ll, 50) + True + >>> pl, ll = gen_rank_fuzzy_counts("paired:list", 100) + >>> pl + 2 + >>> is_around(ll, 50) + True + >>> gen_rank_fuzzy_counts("list:list:list", 1001) + [11, 11, 11] + >>> l1l, l2l, l3l, pl = gen_rank_fuzzy_counts("list:list:list:paired", 2000) + >>> pl + 2 + >>> is_around(10, l1l) + True + >>> gen_rank_fuzzy_counts("list:list:list", 1) + [1, 1, 1] + >>> gen_rank_fuzzy_counts("list:list:list", 2) + [2, 2, 2] + >>> gen_rank_fuzzy_counts("paired:paired", 400) + [2, 2] + >>> gen_rank_fuzzy_counts("paired:paired", 5) + [2, 2] + >>> gen_rank_fuzzy_counts("paired:paired", 3) + [2, 2] + >>> gen_rank_fuzzy_counts("paired:paired", 1) + [1, 1] + >>> gen_rank_fuzzy_counts("paired:paired", 2) + [2, 2] + """ + rank_collection_types = collection_type.split(":") + if fuzzy_count is None: + return [None for rt in rank_collection_types] + else: + # This is a list... + paired_count = sum([1 if rt == "paired" else 0 for rt in rank_collection_types]) + list_count = len(rank_collection_types) - paired_count + paired_fuzzy_count_mult = 1 if paired_count == 0 else 2 << (paired_count - 1) + list_fuzzy_count_mult = math.floor((fuzzy_count * 1.0) / paired_fuzzy_count_mult) + list_rank_fuzzy_count = int(math.floor(math.pow(list_fuzzy_count_mult, 1.0 / list_count)) + 1) if list_count > 0 else 1.0 + pair_rank_fuzzy_count = 2 + if list_rank_fuzzy_count > fuzzy_count: + list_rank_fuzzy_count = fuzzy_count + if pair_rank_fuzzy_count > fuzzy_count: + pair_rank_fuzzy_count = fuzzy_count + rank_fuzzy_counts = [pair_rank_fuzzy_count if rt == "paired" else list_rank_fuzzy_count for rt in rank_collection_types] + + return rank_fuzzy_counts + + __all__ = ('api_payload_to_create_params', 'dictify_dataset_collection_instance') diff --git a/lib/galaxy/webapps/galaxy/api/history_contents.py b/lib/galaxy/webapps/galaxy/api/history_contents.py index 6f954009826..9b11f561780 100644 --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -127,9 +127,9 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary return rval - def __collection_dict(self, trans, dataset_collection_instance, view="collection"): + def __collection_dict(self, trans, dataset_collection_instance, **kwds): return dictify_dataset_collection_instance(dataset_collection_instance, - security=trans.security, parent=dataset_collection_instance.history, view=view) + security=trans.security, parent=dataset_collection_instance.history, **kwds) @expose_api_anonymous def show(self, trans, id, history_id, **kwd): @@ -146,12 +146,27 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary :type history_id: str :param history_id: encoded id string of the HDA's or HDCA's History :type view: str - :param view: view style of the dataset collection to produce, + :param view: if fetching a dataset collection - the view style of + the dataset collection to produce. 'collection' returns no element information, 'element' returns detailed element information for all datasets, 'element-reference' returns a minimal set of information about datasets (for instance id, type, and state but not - metadata, peek, info, or name). + metadata, peek, info, or name). The default is 'element'. + :type fuzzy_count: int + :param fuzzy_count: this value can be used to broadly restrict the magnitude + of the number of elements returned via the API for large + collections. The number of actual elements returned may + be "a bit" more than this number or "a lot" less - varying + on the depth of nesting, balance of nesting at each level, + and size of target collection. The consumer of this API should + not expect a stable number or pre-calculable number of + elements to be produced given this parameter - the only + promise is that this API will not respond with an order + of magnitude more elements estimated with this value. + The UI uses this parameter to fetch a "balanced" concept of + the "start" of large collections at every depth of the + collection. :rtype: dict :returns: dictionary containing detailed HDA or HDCA information @@ -250,7 +265,10 @@ class HistoryContentsController(BaseAPIController, UsesLibraryMixin, UsesLibrary def __show_dataset_collection(self, trans, id, history_id, **kwd): dataset_collection_instance = self.__get_accessible_collection(trans, id, history_id) view = kwd.get("view", "element") - return self.__collection_dict(trans, dataset_collection_instance, view=view) + fuzzy_count = kwd.get("fuzzy_count", None) + if fuzzy_count: + fuzzy_count = int(fuzzy_count) + return self.__collection_dict(trans, dataset_collection_instance, view=view, fuzzy_count=fuzzy_count) def __get_accessible_collection(self, trans, id, history_id): return trans.app.dataset_collections_service.get_dataset_collection_instance( From 4b4a6e5a1c0d248ec32f07157ff6ecc3e7364ef3 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Sat, 4 Nov 2017 15:39:04 -0400 Subject: [PATCH 3/9] Instrumentable paging... --- client/galaxy/scripts/mvc/history/history-contents.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/galaxy/scripts/mvc/history/history-contents.js b/client/galaxy/scripts/mvc/history/history-contents.js index 3cfc4794176..46c46f7fec0 100644 --- a/client/galaxy/scripts/mvc/history/history-contents.js +++ b/client/galaxy/scripts/mvc/history/history-contents.js @@ -6,6 +6,11 @@ import JOB_STATES_MODEL from "mvc/history/job-states-model"; import BASE_MVC from "mvc/base-mvc"; import AJAX_QUEUE from "utils/ajax-queue"; +var limitPerPageDefault = 500; +try { + limitPerPageDefault = localStorage.getItem("historyContentsLimitPerPageDefault") || limitPerPageDefault; +} catch (err) {} + //============================================================================== var _super = CONTROLLED_FETCH_COLLECTION.PaginatedCollection; /** @class Backbone collection for history content. @@ -20,10 +25,10 @@ var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({ _logNamespace: "history", // ........................................................................ set up - limitPerPage: 500, + limitPerPage: limitPerPageDefault, /** @type {Integer} how many contents per call to fetch when using progressivelyFetchDetails */ - limitPerProgressiveFetch: 500, + limitPerProgressiveFetch: limitPerPageDefault, /** @type {String} order used here and when fetching from server */ order: "hid", From 485f64ee38bfeb31601817cac06028c7d043481c Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 20 Nov 2017 23:09:58 -0500 Subject: [PATCH 4/9] Selenium testing for pagination of the history panel. --- test/galaxy_selenium/navigates_galaxy.py | 8 ++++ test/galaxy_selenium/navigation.yml | 7 ++++ .../test_history_panel_pagination.py | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 test/selenium_tests/test_history_panel_pagination.py diff --git a/test/galaxy_selenium/navigates_galaxy.py b/test/galaxy_selenium/navigates_galaxy.py index ddb8b3f5ae9..1d20c179d9a 100644 --- a/test/galaxy_selenium/navigates_galaxy.py +++ b/test/galaxy_selenium/navigates_galaxy.py @@ -154,6 +154,14 @@ class NavigatesGalaxy(HasDriver): def switch_to_main_panel(self): self.driver.switch_to.frame("galaxy_main") + @contextlib.contextmanager + def local_storage(self, key, value): + self.driver.execute_script('''window.localStorage.setItem("%s", %s);''' % (key, value)) + try: + yield + finally: + self.driver.execute_script('''window.localStorage.removeItem("%s");''' % key) + @contextlib.contextmanager def main_panel(self): try: diff --git a/test/galaxy_selenium/navigation.yml b/test/galaxy_selenium/navigation.yml index 2f8f67c0fe8..666948811e8 100644 --- a/test/galaxy_selenium/navigation.yml +++ b/test/galaxy_selenium/navigation.yml @@ -139,6 +139,13 @@ history_panel: options_menu: '#history-options-button-menu' multi_view_button: '#history-view-multi-button' + pagination_pages: '.list-pagination .pages' + pagination_pages_options: '.list-pagination .pages option' + pagination_pages_selected_option: '.list-pagination .pages option:checked' + pagination_next: '.list-pagination button.next' + pagination_previous: '.list-pagination button.prev' + + text: tooltip_name: 'Click to rename history' new_name: 'Unnamed history' diff --git a/test/selenium_tests/test_history_panel_pagination.py b/test/selenium_tests/test_history_panel_pagination.py new file mode 100644 index 00000000000..d1ad0184368 --- /dev/null +++ b/test/selenium_tests/test_history_panel_pagination.py @@ -0,0 +1,42 @@ +from .framework import ( + selenium_test, + SeleniumTestCase +) + + +class HistoryPanelPaginationTestCase(SeleniumTestCase): + + ensure_registered = True + + @selenium_test + def test_pagination(self): + history_id = self.current_history_id() + + self.dataset_populator.new_dataset(history_id, content='1\t2\t3', name="data1") + self.dataset_populator.new_dataset(history_id, content='2\t3\t4', name="data2") + self.dataset_populator.new_dataset(history_id, content='3\t4\t5', name="data3") + self.dataset_populator.new_dataset(history_id, content='4\t5\t6', name="data4") + self.dataset_populator.new_dataset(history_id, content='5\t6\t7', name="data5") + + self.home() + for hid in [1, 2, 3, 4, 5]: + self.history_panel_wait_for_hid_state(hid, "ok") + + with self.local_storage("historyContentsLimitPerPageDefault", 3): + self.home() + self.history_panel_wait_for_hid_state(5, "ok") + self.screenshot("history_panel_pagination_initial") + pagination_option_text = self.components.history_panel.pagination_pages_selected_option.wait_for_text() + assert "1st of 2 pages" in pagination_option_text + self.components.history_panel.pagination_pages.wait_for_and_click() + self.screenshot("history_panel_pagination_pages_drop_down") + self.components.history_panel.pagination_next.wait_for_and_click() + self.sleep_for(self.wait_types.UX_TRANSITION) + self.screenshot("history_panel_pagination_second") + pagination_option_text = self.components.history_panel.pagination_pages_selected_option.wait_for_text() + assert "2nd of 2 pages" in pagination_option_text + self.components.history_panel.pagination_previous.wait_for_and_click() + + self.sleep_for(self.wait_types.UX_TRANSITION) + pagination_option_text = self.components.history_panel.pagination_pages_selected_option.wait_for_text() + assert "1st of 2 pages" in pagination_option_text From 2a165c9afce58a8428bbdffaa0a5887e57285272 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 29 Nov 2017 07:29:34 -0500 Subject: [PATCH 5/9] Refactor subtitle of collection display. --- .../scripts/mvc/collection/collection-view.js | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/client/galaxy/scripts/mvc/collection/collection-view.js b/client/galaxy/scripts/mvc/collection/collection-view.js index 2f2a93af9fb..d21656d0dfe 100644 --- a/client/galaxy/scripts/mvc/collection/collection-view.js +++ b/client/galaxy/scripts/mvc/collection/collection-view.js @@ -149,45 +149,41 @@ var CollectionView = _super.extend( //------------------------------------------------------------------------------ TEMPLATES CollectionView.prototype.templates = (() => { - var controlsTemplate = BASE_MVC.wrapTemplate( - [ - '
', - '", - - '
', - '
<%- collection.name || collection.element_identifier %>
', - '
', - '<% if( collection.collection_type === "list" ){ %>', - _l("a list of datasets"), - '<% } else if( collection.collection_type === "paired" ){ %>', - _l("a pair of datasets"), - '<% } else if( collection.collection_type === "list:paired" ){ %>', - _l("a list of paired datasets"), - '<% } else if( collection.collection_type === "list:list" ){ %>', - _l("a list of dataset lists"), - "<% } %>", - "
", - "
", - - '
', - - '
', - '', - '', - "", - "
", - "
" - ], - "collection" - ); + var controlsTemplate = (collection, view) => { + var subtitle; + if (collection.collection_type === "list") { + subtitle = _l("a list of datasets"); + } else if (collection.collection_type === "paired") { + subtitle = _l("a pair of datasets"); + } else if (collection.collection_type === "list:paired") { + subtitle = _l("a list of paired datasets"); + } else if (collection.collection_type === "list:list") { + subtitle = _l("a nested list"); + } + return ` +
+ +
+
${_.escape(collection.name) || _.escape(collection.element_identifier)}
+
+ ${subtitle} +
+
+
+
+ + + +
+
`; + }; return _.extend(_.clone(_super.prototype.templates), { controls: controlsTemplate From 98e5d932f980c0e60f72d6d7184fea145deef3a9 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 29 Nov 2017 07:58:34 -0500 Subject: [PATCH 6/9] Synchronize logic/code for displaying short collection descriptions. --- .../scripts/mvc/collection/collection-view.js | 44 ++++++++++++++----- client/galaxy/scripts/mvc/history/hdca-li.js | 27 +++--------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/client/galaxy/scripts/mvc/collection/collection-view.js b/client/galaxy/scripts/mvc/collection/collection-view.js index d21656d0dfe..06fdbc458dd 100644 --- a/client/galaxy/scripts/mvc/collection/collection-view.js +++ b/client/galaxy/scripts/mvc/collection/collection-view.js @@ -150,16 +150,7 @@ var CollectionView = _super.extend( //------------------------------------------------------------------------------ TEMPLATES CollectionView.prototype.templates = (() => { var controlsTemplate = (collection, view) => { - var subtitle; - if (collection.collection_type === "list") { - subtitle = _l("a list of datasets"); - } else if (collection.collection_type === "paired") { - subtitle = _l("a pair of datasets"); - } else if (collection.collection_type === "list:paired") { - subtitle = _l("a list of paired datasets"); - } else if (collection.collection_type === "list:list") { - subtitle = _l("a nested list"); - } + var subtitle = collectionDescription(view.model); return `
+
+
Date: Thu, 30 Nov 2017 15:59:28 -0500 Subject: [PATCH 8/9] Make new Selenium pagination test as flakey - doesn't work consistently on Jenkins. --- test/selenium_tests/test_history_panel_pagination.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/selenium_tests/test_history_panel_pagination.py b/test/selenium_tests/test_history_panel_pagination.py index d1ad0184368..f2a2c62c738 100644 --- a/test/selenium_tests/test_history_panel_pagination.py +++ b/test/selenium_tests/test_history_panel_pagination.py @@ -1,3 +1,5 @@ +from base.populators import flakey + from .framework import ( selenium_test, SeleniumTestCase @@ -9,6 +11,7 @@ class HistoryPanelPaginationTestCase(SeleniumTestCase): ensure_registered = True @selenium_test + @flakey # The next button doesn't always work - maybe a delay in JS callback registering for that. def test_pagination(self): history_id = self.current_history_id() From 94a5cc141dabce5e95044517550a80b2d60e44f6 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 30 Nov 2017 16:22:33 -0500 Subject: [PATCH 9/9] Repack client. --- static/scripts/bundled/analysis.bundled.js | 4 ++-- static/scripts/mvc/collection/collection-view.js | 2 +- static/scripts/mvc/history/hdca-li.js | 2 +- static/scripts/mvc/history/history-content-model.js | 2 +- static/scripts/mvc/history/history-contents.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/static/scripts/bundled/analysis.bundled.js b/static/scripts/bundled/analysis.bundled.js index fe11d07a23a..77b2ad7edcf 100644 --- a/static/scripts/bundled/analysis.bundled.js +++ b/static/scripts/bundled/analysis.bundled.js @@ -4,6 +4,6 @@ webpackJsonp([0],[,,,,function(e,t,i){"use strict";(function(e,n,a){function s(e * @author Feross Aboukhadijeh * @license MIT */ -e.exports=function(e){return null!=e&&(i(e)||n(e)||!!e._isBuffer)}},function(e,t,i){"use strict";function n(e){this.defaults=e,this.interceptors={request:new o,response:new o}}var a=i(16),s=i(5),o=i(85),r=i(86);n.prototype.request=function(e){"string"==typeof e&&(e=s.merge({url:arguments[0]},arguments[1])),e=s.merge(a,this.defaults,{method:"get"},e),e.method=e.method.toLowerCase();var t=[r,void 0],i=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)i=i.then(t.shift(),t.shift());return i},s.forEach(["delete","get","head","options"],function(e){n.prototype[e]=function(t,i){return this.request(s.merge(i||{},{method:e,url:t}))}}),s.forEach(["post","put","patch"],function(e){n.prototype[e]=function(t,i,n){return this.request(s.merge(n||{},{method:e,url:t,data:i}))}}),e.exports=n},function(e,t){function i(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function a(e){if(c===setTimeout)return setTimeout(e,0);if((c===i||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function s(e){if(u===clearTimeout)return clearTimeout(e);if((u===n||!u)&&clearTimeout)return u=clearTimeout,clearTimeout(e);try{return u(e)}catch(t){try{return u.call(null,e)}catch(t){return u.call(this,e)}}}function o(){m&&f&&(m=!1,f.length?p=f.concat(p):_=-1,p.length&&r())}function r(){if(!m){var e=a(o);m=!0;for(var t=p.length;t;){for(f=p,p=[];++_1)for(var i=1;i=0)return;o[t]="set-cookie"===t?(o[t]?o[t]:[]).concat([i]):o[t]?o[t]+", "+i:i}}),o):o}},function(e,t,i){"use strict";var n=i(5);e.exports=n.isStandardBrowserEnv()?function(){function e(e){var t=e;return i&&(a.setAttribute("href",t),t=a.href),a.setAttribute("href",t),{href:a.href,protocol:a.protocol?a.protocol.replace(/:$/,""):"",host:a.host,search:a.search?a.search.replace(/^\?/,""):"",hash:a.hash?a.hash.replace(/^#/,""):"",hostname:a.hostname,port:a.port,pathname:"/"===a.pathname.charAt(0)?a.pathname:"/"+a.pathname}}var t,i=/(msie|trident)/i.test(navigator.userAgent),a=document.createElement("a");return t=e(window.location.href),function(i){var a=n.isString(i)?e(i):i;return a.protocol===t.protocol&&a.host===t.host}}():function(){return function(){return!0}}()},function(e,t,i){"use strict";function n(){this.message="String contains an invalid character"}function a(e){for(var t,i,a=String(e),o="",r=0,l=s;a.charAt(0|r)||(l="=",r%1);o+=l.charAt(63&t>>8-r%1*8)){if((i=a.charCodeAt(r+=.75))>255)throw new n;t=t<<8|i}return o}var s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";n.prototype=new Error,n.prototype.code=5,n.prototype.name="InvalidCharacterError",e.exports=a},function(e,t,i){"use strict";var n=i(5);e.exports=n.isStandardBrowserEnv()?function(){return{write:function(e,t,i,a,s,o){var r=[];r.push(e+"="+encodeURIComponent(t)),n.isNumber(i)&&r.push("expires="+new Date(i).toGMTString()),n.isString(a)&&r.push("path="+a),n.isString(s)&&r.push("domain="+s),!0===o&&r.push("secure"),document.cookie=r.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,i){"use strict";function n(){this.handlers=[]}var a=i(5);n.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},n.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},n.prototype.forEach=function(e){a.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=n},function(e,t,i){"use strict";function n(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var a=i(5),s=i(87),o=i(30),r=i(16),l=i(88),d=i(89);e.exports=function(e){return n(e),e.baseURL&&!l(e.url)&&(e.url=d(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=a.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),a.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]}),(e.adapter||r.adapter)(e).then(function(t){return n(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return o(t)||(n(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,i){"use strict";var n=i(5);e.exports=function(e,t,i){return n.forEach(i,function(i){e=i(e,t)}),e}},function(e,t,i){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,i){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,i){"use strict";function n(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var i=this;e(function(e){i.reason||(i.reason=new a(e),t(i.reason))})}var a=i(31);n.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},n.source=function(){var e;return{token:new n(function(t){e=t}),cancel:e}},e.exports=n},function(e,t,i){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,i){!function(e){function t(){this.months=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"],this.notKey=[",","{","}"," ","="],this.pos=0,this.input="",this.entries=new Array,this.currentEntry="",this.setInput=function(e){this.input=e},this.getEntries=function(){return this.entries},this.isWhitespace=function(e){return" "==e||"\r"==e||"\t"==e||"\n"==e},this.match=function(e,t){if(void 0!=t&&null!=t||(t=!0),this.skipWhitespace(t),this.input.substring(this.pos,this.pos+e.length)!=e)throw"Token mismatch, expected "+e+", found "+this.input.substring(this.pos);this.pos+=e.length,this.skipWhitespace(t)},this.tryMatch=function(e,t){return void 0!=t&&null!=t||(t=!0),this.skipWhitespace(t),this.input.substring(this.pos,this.pos+e.length)==e},this.matchAt=function(){for(;this.input.length>this.pos&&"@"!=this.input[this.pos];)this.pos++;return"@"==this.input[this.pos]},this.skipWhitespace=function(e){for(;this.isWhitespace(this.input[this.pos]);)this.pos++;if("%"==this.input[this.pos]&&1==e){for(;"\n"!=this.input[this.pos];)this.pos++;this.skipWhitespace(e)}},this.value_braces=function(){var e=0;this.match("{",!1);for(var t=this.pos,i=!1;;){if(!i)if("}"==this.input[this.pos]){if(!(e>0)){var n=this.pos;return this.match("}",!1),this.input.substring(t,n)}e--}else if("{"==this.input[this.pos])e++;else if(this.pos>=this.input.length-1)throw"Unterminated value";i="\\"==this.input[this.pos]&&0==i,this.pos++}},this.value_comment=function(){for(var e="",t=0;!this.tryMatch("}",!1)||0!=t;){if(e+=this.input[this.pos],"{"==this.input[this.pos]&&t++,"}"==this.input[this.pos]&&t--,this.pos>=this.input.length-1)throw"Unterminated value:"+this.input.substring(start);this.pos++}return e},this.value_quotes=function(){this.match('"',!1);for(var e=this.pos,t=!1;;){if(!t){if('"'==this.input[this.pos]){var i=this.pos;return this.match('"',!1),this.input.substring(e,i)}if(this.pos>=this.input.length-1)throw"Unterminated value:"+this.input.substring(e)}t="\\"==this.input[this.pos]&&0==t,this.pos++}},this.single_value=function(){var e=this.pos;if(this.tryMatch("{"))return this.value_braces();if(this.tryMatch('"'))return this.value_quotes();var t=this.key();if(t.match("^[0-9]+$"))return t;if(this.months.indexOf(t.toLowerCase())>=0)return t.toLowerCase();throw"Value expected:"+this.input.substring(e)+" for key: "+t},this.value=function(){var e=[];for(e.push(this.single_value());this.tryMatch("#");)this.match("#"),e.push(this.single_value());return e.join("")},this.key=function(e){for(var t=this.pos;;){if(this.pos>=this.input.length)throw"Runaway key";if(this.notKey.indexOf(this.input[this.pos])>=0)return e&&","!=this.input[this.pos]?(this.pos=t,null):this.input.substring(t,this.pos);this.pos++}},this.key_equals_value=function(){var e=this.key();if(this.tryMatch("=")){this.match("=");var t=this.value();return e=e.trim(),[e,t]}throw"... = value expected, equals sign missing:"+this.input.substring(this.pos)},this.key_value_list=function(){var e=this.key_equals_value();for(this.currentEntry.entryTags={},this.currentEntry.entryTags[e[0]]=e[1];this.tryMatch(",")&&(this.match(","),!this.tryMatch("}"));)e=this.key_equals_value(),this.currentEntry.entryTags[e[0]]=e[1]},this.entry_body=function(e){this.currentEntry={},this.currentEntry.citationKey=this.key(!0),this.currentEntry.entryType=e.substring(1),null!=this.currentEntry.citationKey&&this.match(","),this.key_value_list(),this.entries.push(this.currentEntry)},this.directive=function(){return this.match("@"),"@"+this.key()},this.preamble=function(){this.currentEntry={},this.currentEntry.entryType="PREAMBLE",this.currentEntry.entry=this.value_comment(),this.entries.push(this.currentEntry)},this.comment=function(){this.currentEntry={},this.currentEntry.entryType="COMMENT",this.currentEntry.entry=this.value_comment(),this.entries.push(this.currentEntry)},this.entry=function(e){this.entry_body(e)},this.alernativeCitationKey=function(){this.entries.forEach(function(e){!e.citationKey&&e.entryTags&&(e.citationKey="",e.entryTags.author&&(e.citationKey+=e.entryTags.author.split(",")[0]+=", "),e.citationKey+=e.entryTags.year)})},this.bibtex=function(){for(;this.matchAt();){var e=this.directive();this.match("{"),"@STRING"==e.toUpperCase()?this.string():"@PREAMBLE"==e.toUpperCase()?this.preamble():"@COMMENT"==e.toUpperCase()?this.comment():this.entry(e),this.match("}")}this.alernativeCitationKey()}}e.toJSON=function(e){var i=new t;return i.setInput(e),i.bibtex(),i.entries},e.toBibtex=function(e){var t="";for(var i in e){if(t+="@"+e[i].entryType,t+="{",e[i].citationKey&&(t+=e[i].citationKey+", "),e[i].entry&&(t+=e[i].entry),e[i].entryTags){var n="";for(var a in e[i].entryTags)0!=n.length&&(n+=", "),n+=a+"= {"+e[i].entryTags[a]+"}";t+=n}t+="}\n\n"}return t}}(t)},function(e,t,i){"use strict";function n(e){for(var i in e)t.hasOwnProperty(i)||(t[i]=e[i])}Object.defineProperty(t,"__esModule",{value:!0}),n(i(94)),n(i(95)),n(i(32)),n(i(35)),n(i(10)),n(i(36)),n(i(136)),n(i(137)),n(i(39))},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.aliases={mathfrak:"frak",mathcal:"cal",mathbb:"bb",mathbf:"bf",dfrac:"frac",ldots:"dots"}},function(e,t,i){"use strict";function n(e,t){return s(e,r.mustNotBeUndefined(r.mustBeOk(r.latexParser.parse(t)).value))}function a(e){return s({translateTo:"unicode",mode:"Any"},r.mustNotBeUndefined(r.mustBeOk(r.latexParser.parse(e)).value))}function s(e,t){var i=e.translateTo;switch(i){case"html":throw new Error("Unsupported format: '"+i+"'. Use one of: "+Object.keys(o.supportedMarkups));case"unicode":default:return l.convertLaTeXBlocksToUnicode(e,t).result}}Object.defineProperty(t,"__esModule",{value:!0});var o=i(32),r=i(17),l=i(97);t.convertLaTeX=n,t.convertLaTeXToUnicode=a,t.convertLaTeXBlocks=s},function(e,t,i){"use strict";function n(e,t){var i=e.length,n=t.length;if(0===i)return t;if(0===n)return e;for(var a={},s=0;st.furthest)return e;var i=e.furthest===t.furthest?n(e.expected,t.expected):t.expected;return{status:e.status,index:e.index,value:e.value,furthest:t.furthest,expected:i}}function s(e,t,i,n){return k.Parser(function(s,o){for(var r=n,l=0,d=void 0;o=t.length||e(n))return T.makeFailure(i,"text character");var a=[n];i++;for(var s=t.charAt(i);!e(s)&&i="A"&&e<="Z"}function f(e){return e>="a"&&e<="z"}function p(e){return z.then(o(u(e,"_"),j)).map(C.newFixArg)}function m(e){return O.then(o(u(e),A)).map(C.newOptArg)}function _(e){return k.alt(p(e),m(e))}function g(e){return k.alt(k.string("{}").map(function(){return[]}),_(e).map(function(e){return e}).atLeast(0)).map(function(e){return e})}function v(e){return k.seqMap(t.commandSymbol,k.alt(t.specialChar,t.takeTill(t.endCmd)),g(e),function(e,t,i){return void 0!==i?C.newTeXComm.apply(void 0,[t].concat(i)):C.newTeXComm(t)}).map(function(e){return e})}function w(e,i,n){return k.seqMap(t.subOrSuperscriptSymbolParser(i,n),g(e),function(e,t){return C.newSubOrSuperScript(e,e===C.SubOrSuperSymbol.SUB?i:n,t)}).map(function(e){return e})}function b(e){return void 0!==e&&!0===e.status}function y(e){return void 0!==e&&!1===e.status}function x(e){if(!b(e))throw new Error("Expected parse to be success: "+JSON.stringify(e));return e}Object.defineProperty(t,"__esModule",{value:!0});var k=i(34),C=i(19),$=i(19),S=i(18),T=i(34);t.defaultParserConf={verbatimEnvironments:["verbatim"]},t.takeTill=function(e){return k.takeWhile(function(t){return!e(t)})};var M=k.regexp(/[^\n]*/),E=k.regexp(/\n?/),P=(k.regexp(/\s*/m),k.string("%")),O=k.string("["),A=k.string("]");t.notTextDefault={$:!0,"%":!0,"\\":!0,"{":!0,"]":!0,"}":!0},t.notTextMathMode={"^":!0,_:!0,$:!0,"%":!0,"\\":!0,"{":!0,"]":!0,"}":!0},t.notTextMathModeAndNotClosingBracket={"^":!0,_:!0,$:!0,"%":!0,"\\":!0,"{":!0,"}":!0},t.notTextDefaultAndNotClosingBracket={$:!0,"%":!0,"\\":!0,"{":!0,"}":!0},t.textParser=l;var D=(l(t.notTextDefault),l(t.notTextDefaultAndNotClosingBracket),k.regexp(/ */).map($.newTeXRaw));t.comment=P.then(M).skip(E).map(C.newTeXComment),t.specialCharsDefault={"'":!0,"(":!0,")":!0,",":!0,".":!0,"-":!0,'"':!0,"!":!0,"^":!0,$:!0,"&":!0,"#":!0,"{":!0,"}":!0,"%":!0,"~":!0,"|":!0,"/":!0,":":!0,";":!0,"=":!0,"[":!0,"]":!0,"\\":!0,"`":!0," ":!0},t.isSpecialCharacter=d,t.isNotText=c,t.mathSymbol=k.string("$"),t.commandSymbol=k.string("\\"),t.latexBlockParser=u,t.latexBlockParserTextMode=k.lazy(function(){return k.alt(k.alt(l(t.notTextDefault),t.dolMath,t.comment,l(t.notTextDefaultAndNotClosingBracket),t.environment,v("Paragraph")))}),t.latexBlockParserMathMode=function(e,i){return k.lazy(function(){return k.alt(k.alt(w("Math",e,i),l(t.notTextMathMode),t.dolMath,t.comment,l(t.notTextMathModeAndNotClosingBracket),t.environment,v("Math")))})},t.latexParser=t.latexBlockParserTextMode.many();var I=k.string("{").then(t.latexBlockParserTextMode.many()).skip(k.string("}"));t.env=k.Parser(function(e,i){var n=k.string("\\begin").then(k.string("{")).then(D).then(k.regexp(/[a-zA-Z]+/)).skip(D).skip(k.string("}"))._(e,i);if(y(n))return n;i=S.mustBeNumber(n.index);var a=n.value;return o(t.latexBlockParserTextMode,k.string("\\end").then(k.string("{")).then(D).then(k.string(a)).then(D).then(k.string("}"))).map(function(e){return C.newTeXEnv(a,e)})._(e,i)}),t.environment=k.alt(I,t.env),t.specialChar=k.test(d),t.endCmd=function(e){return!f(e)&&!h(e)};var z=k.string("{"),j=k.string("}");t.fixArg=p,t.optArg=m,t.cmdArg=_,t.cmdArgs=g,t.command=v,t.subOrSuperscriptSymbolParser=function(e,t){return k.alt(k.string(e),k.string(t)).map(function(t){return t===e?C.SubOrSuperSymbol.SUB:C.SubOrSuperSymbol.SUP})},t.shiftedScript=w,t.dolMath=function(e,t,i){return void 0===e&&(e="Dollar"),void 0===t&&(t="$"),void 0===i&&(i="$"),k.string(t).then(u("Math","_").many().map(function(n){return C.newTeXMath(e,t,i,n)})).skip(k.string(i))}(),t.isOk=b,t.isNotOk=y,t.mustBeOk=x},function(e,t,i){"use strict";function n(e){return"string"==typeof e}function a(e,t){var i=e;do{e++}while(h.isTeXChar(t[e]));return{result:t.slice(i,e).map(function(e){return e.string}).join(""),blockIndex:e}}function s(e,t,i,a){var s=u(e,a);if(n(s))return{result:s,blockIndex:t+1};for(var o=[],l=[];o.length=0&&l.push(p.substring(m))}else o.push(c)}t++;var w=o.map(function(t){return r(e,[t]).result}).map(h.newTeXRaw).map(function(e){return h.newFixArg([e])});if(w.length0&&b.push(l.join("")),{result:b.join(""),blockIndex:t}}function o(e){return void 0!==e&&"string"==typeof e.string&&"number"==typeof e.category}function r(e,t){var i=0;if(t.length<=0)return{result:"",blockIndex:i};for(var n=[];i0){var i=_.expand1argsCommand(t.name,r(e,[t.arguments[0]]).result||"");return t.arguments.length>1?i+r(e,t.arguments.slice(1)).result:i}return m.createCommandHandler(t.name,0,1,function(i,n){var a=n[0],s=n.slice(1);return _.expand1argsCommand(t.name,r(e,[a]).result)+r(e,s).result})}function d(e,t){if(t.arguments.length>1){var i=w.expand2argsCommand(t.name,r(e,[t.arguments[0]]).result||"",r(e,[t.arguments[1]]).result||"");return t.arguments.length>2?i+r(e,t.arguments.slice(1)).result:i}return m.createCommandHandler(t.name,0,2,function(i,n){var a=n[0],s=n[1],o=n.slice(2);return w.expand2argsCommand(t.name,r(e,[a]).result,r(e,[s]).result)+r(e,o).result})}function c(e,t){for(var i=void 0,n=void 0,a=0;void 0===n&&a1){var s=i.slice(1);return a+r(e,s).result}return a})}function u(e,t){var i=t.name,n=p.expand0argsCommand(i);if(n)return t.arguments&&t.arguments.length>0?n+r(e,t.arguments).result:n;if(g.is1argsCommand(i))return l(e,t);if(b.is2argsCommand(i))return d(e,t);if("sqrt"===i)return c(e,t);throw f.unknownCommandError(i)}Object.defineProperty(t,"__esModule",{value:!0});var h=i(17),f=i(35),p=i(98),m=i(36),_=i(105),g=i(39),v=i(130),w=i(131),b=i(134),y=i(33),x=/^\s*/;t.isTeXChar2=o,t.convertLaTeXBlocksToUnicode=r,t.convertCommand=u},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(99),a=i(100),s=i(101),o=i(102),r=i(103),l=i(104);t.expand0argsCommand=function(e){for(var t=0,i=[s.barredLUnicode,n.spaceUnicode,o.slashedOUnicode,a.characterUnicode,l.specialCharacter,r.cyrillicUnicode];t",thickapprox:"≈",updownarrow:"↕",vartriangle:"△",Lleftarrow:"⇚",Rightarrow:"⇒",circledast:"⊛",complement:"∁",curlywedge:"⋏",longmapsto:"⟼",registered:"®",rightarrow:"→",smallfrown:"⌢",smallsmile:"⌣",sqsubseteq:"⊑",sqsupseteq:"⊒",textlangle:"〈",textrangle:"〉",upuparrows:"⇈",varepsilon:"ε",varnothing:"∅",Downarrow:"⇓",Leftarrow:"⇐",backprime:"‵",bigotimes:"⨂",centerdot:"⋅",copyright:"©",downarrow:"↓",gtreqless:"⋛",heartsuit:"♡",leftarrow:"←",lesseqgtr:"⋚",pitchfork:"⋔",spadesuit:"♠",therefore:"∴",trademark:"™",triangleq:"≜",varpropto:"∝",approxeq:"≊",barwedge:"⊼",bigoplus:"⨁",bigsqcup:"⨆",biguplus:"⨄",bigwedge:"⋀",boxminus:"⊟",boxtimes:"⊠",circledS:"Ⓢ",clubsuit:"♣",curlyvee:"⋎",doteqdot:"≑",emptyset:"∅",intercal:"⊺",leqslant:"⩽",multimap:"⊸",parallel:"∥",setminus:"∖",sqsubset:"⊏",sqsupset:"⊐",subseteq:"⊆",supseteq:"⊇",textless:"<",thicksim:"∼",triangle:"△",varkappa:"ϰ",varsigma:"ς",vartheta:"ϑ",Diamond:"◇",Uparrow:"⇑",Upsilon:"Υ",backsim:"∽",because:"∵",between:"≬",bigodot:"⨀",bigstar:"★",boxplus:"⊞",ddagger:"‡",diamond:"⋄",digamma:"Ϝ",dotplus:"∔",epsilon:"∊",gtrless:"≷",implies:"⇒",leadsto:"↝",lessdot:"⋖",lessgtr:"≶",lesssim:"≲",lozenge:"◊",natural:"♮",nearrow:"↗",nexists:"∄",nwarrow:"↖",partial:"∂",pilcrow:"¶",precsim:"≾",searrow:"↘",section:"§",succsim:"≿",swarrow:"↙",textbar:"|",uparrow:"↑",upsilon:"υ",Bumpeq:"≎",Lambda:"Λ",Subset:"⋐",Supset:"⋑",Vvdash:"⊪",approx:"≈",bigcap:"⋂",bigcup:"⋃",bigvee:"⋁",bowtie:"⋈",boxdot:"⊡",bullet:"∙",bumpeq:"≏",circeq:"≗",coprod:"∐",dagger:"†",daleth:"ד",degree:"°",eqcirc:"≖",exists:"∃",forall:"∀",gtrdot:"⋗",gtrsim:"≳",hslash:"ℏ",lambda:"λ",lfloor:"⌊",ltimes:"⋉",mapsto:"↦",models:"⊨",ominus:"⊖",oslash:"⊘",otimes:"⊗",preceq:"⪯",propto:"∝",rfloor:"⌋",rtimes:"⋊",square:"□",subset:"⊂",succeq:"⪰",supset:"⊃",varphi:"φ",varrho:"ϱ",veebar:"⊻",Delta:"Δ",Gamma:"Γ",Omega:"Ω",Theta:"Θ",Vdash:"⊩",aleph:"ℵ",Alpha:"Α",alpha:"α",angle:"∠",asymp:"≍",cdots:"⋯",cents:"¢",dashv:"⊣",ddots:"⋱",delta:"δ",doteq:"≐",equiv:"≡",frown:"⌢",gamma:"γ",gimel:"ℷ",infty:"∞",kappa:"κ",Kappa:"Κ",lceil:"⌈",nabla:"∇",notin:"∉",omega:"ω",oplus:"⊕",pound:"£",prime:"′",qquad:"  ",rceil:"⌉",sharp:"♯",sigma:"σ",simeq:"≃",smile:"⌣",space:"␣",sqcap:"⊓",sqcup:"⊔",theta:"θ",times:"×",unlhd:"⊴",unrhd:"⊵",uplus:"⊎",vDash:"⊨",varpi:"ϖ",vdash:"⊢",vdots:"⋮",wedge:"∧",Finv:"Ⅎ",Join:"⋈",atop:"¦",beta:"β",Beta:"Β",beth:"ב",cdot:"⋅",circ:"∘",cong:"≅",dots:"…",euro:"€",flat:"♭",geqq:"≧",hbar:"ℏ",iota:"ι",leqq:"≦",odot:"⊙",oint:"∮",perp:"⊥",prec:"≺",prod:"∏",quad:" ",star:"⋆",succ:"≻",surd:"√",zeta:"ζ",Box:"□",Cap:"⋒",Cup:"⋓",Lsh:"↰",Phi:"Φ",Psi:"Ψ",Rsh:"↱",ast:"∗",bot:"⊥",cap:"∩",chi:"χ",Chi:"Χ",cup:"∪",div:"÷",ell:"ℓ",eta:"η",eth:"ð",geq:"≥",ggg:"⋙",int:"∫",leq:"≤",lhd:"⊲",lll:"⋘",mho:"℧",mid:"∣",neg:"¬",neq:"≠",phi:"ϕ",psi:"ψ",rhd:"⊳",rho:"ρ",Rho:"Ρ",sim:"∼",sum:"∑",tau:"τ",Tau:"Τ",top:"⊤",vee:"∨",Im:"ℑ",Pi:"Π",Re:"ℜ",Xi:"Ξ",ge:"≥",gg:"≫",in:"∈",le:"≤",ll:"≪",mp:"∓",mu:"μ",Mu:"Μ",ni:"∋",nu:"ν",Nu:"Ν",pi:"π",pm:"±",wp:"℘",wr:"≀",xi:"ξ",Omicron:"Ο",omicron:"ο",textdollar:"$",textquotesingle:"'",textbackslash:"\\",textasciigrave:"`",lbrace:"{",vert:"|",rbrace:"}",textasciitilde:"~",textexclamdown:"¡",textcent:"¢",textsterling:"£",textcurrency:"¤",textyen:"¥",textbrokenbar:"¦",textsection:"§",textasciidieresis:"¨",textcopyright:"©",textordfeminine:"ª",guillemotleft:"«",lnot:"¬",textasciimacron:"¯",textdegree:"°",textasciiacute:"´",textparagraph:"¶",textordmasculine:"º",guillemotright:"»",textonequarter:"¼",textonehalf:"½",textthreequarters:"¾",textquestiondown:"¿",AA:"Å",AE:"Æ",DH:"Ð",texttimes:"×",TH:"Þ",ss:"ß",aa:"å",ae:"æ",dh:"ð",th:"þ",DJ:"Đ",dj:"đ",Elzxh:"ħ",i:"ı",NG:"Ŋ",ng:"ŋ",OE:"Œ",oe:"œ",texthvlig:"ƕ",textnrleg:"ƞ",textdoublepipe:"ǂ",Elztrna:"ɐ",Elztrnsa:"ɒ",Elzopeno:"ɔ",Elzrtld:"ɖ",Elzschwa:"ə",Elzpgamma:"ɣ",Elzpbgam:"ɤ",Elztrnh:"ɥ",Elzbtdl:"ɬ",Elzrtll:"ɭ",Elztrnm:"ɯ",Elztrnmlr:"ɰ",Elzltlmr:"ɱ",Elzltln:"ɲ",Elzrtln:"ɳ",Elzclomeg:"ɷ",textphi:"ɸ",Elztrnr:"ɹ",Elztrnrl:"ɺ",Elzrttrnr:"ɻ",Elzrl:"ɼ",Elzrtlr:"ɽ",Elzfhr:"ɾ",Elzrtls:"ʂ",Elzesh:"ʃ",Elztrnt:"ʇ",Elzrtlt:"ʈ",Elzpupsil:"ʊ",Elzpscrv:"ʋ",Elzinvv:"ʌ",Elzinvw:"ʍ",Elztrny:"ʎ",Elzrtlz:"ʐ",Elzyogh:"ʒ",Elzglst:"ʔ",Elzreglst:"ʕ",Elzinglst:"ʖ",textturnk:"ʞ",Elzdyogh:"ʤ",Elztesh:"ʧ",textasciicaron:"ˇ",Elzverts:"ˈ",Elzverti:"ˌ",Elzlmrk:"ː",Elzhlmrk:"ˑ",Elzsbrhr:"˒",Elzsblhr:"˓",Elzrais:"˔",Elzlow:"˕",textasciibreve:"˘",textperiodcentered:"˙",texttildelow:"˜",Epsilon:"Ε",Zeta:"Ζ",Eta:"Η",Iota:"Ι",Sigma:"Σ",texttheta:"θ",textvartheta:"ϑ",Stigma:"Ϛ",Digamma:"Ϝ",Koppa:"Ϟ",Sampi:"Ϡ",textTheta:"ϴ",textendash:"–",textemdash:"—",Vert:"‖",Elzreapos:"‛",textquotedblleft:"“",textquotedblright:"”",textdagger:"†",textdaggerdbl:"‡",textbullet:"•",ldots:"…",textperthousand:"‰",textpertenthousand:"‱",guilsinglleft:"‹",guilsinglright:"›",nolinebreak:"⁠",Elzxrat:"℞",nleftarrow:"↚",nrightarrow:"↛",arrowwaveleft:"↜",arrowwaveright:"↝",nleftrightarrow:"↮",dblarrowupdown:"⇅",nLeftarrow:"⇍",nLeftrightarrow:"⇎",nRightarrow:"⇏",DownArrowUpArrow:"⇵",rightangle:"∟",nmid:"∤",nparallel:"∦",surfintegral:"∯",volintegral:"∰",clwintegral:"∱",Colon:"∷",homothetic:"∻",lazysinv:"∾",NotEqualTilde:"≂",approxnotequal:"≆",tildetrpl:"≋",allequal:"≌",NotHumpDownHump:"≎",NotHumpEqual:"≏",estimates:"≙",starequal:"≛",lneqq:"≨",lvertneqq:"≨",gneqq:"≩",gvertneqq:"≩",NotLessLess:"≪",NotGreaterGreater:"≫",lessequivlnt:"≲",greaterequivlnt:"≳",notlessgreater:"≸",notgreaterless:"≹",precapprox:"≾",NotPrecedesTilde:"≾",succapprox:"≿",NotSucceedsTilde:"≿",subsetneq:"⊊",varsubsetneqq:"⊊",supsetneq:"⊋",varsupsetneq:"⊋",NotSquareSubset:"⊏",NotSquareSuperset:"⊐",truestate:"⊧",forcesextra:"⊨",VDash:"⊫",nvdash:"⊬",nvDash:"⊭",nVdash:"⊮",nVDash:"⊯",original:"⊶",image:"⊷",hermitconjmatrix:"⊹",rightanglearc:"⊾",backsimeq:"⋍",verymuchless:"⋘",verymuchgreater:"⋙",Elzsqspne:"⋥",lnsim:"⋦",gnsim:"⋧",precedesnotsimilar:"⋨",succnsim:"⋩",ntriangleleft:"⋪",ntriangleright:"⋫",ntrianglelefteq:"⋬",ntrianglerighteq:"⋭",upslopeellipsis:"⋰",downslopeellipsis:"⋱",perspcorrespond:"⌆",recorder:"⌕",ulcorner:"⌜",urcorner:"⌝",llcorner:"⌞",lrcorner:"⌟",langle:"〈",rangle:"〉",Elzdlcorn:"⎣",lmoustache:"⎰",rmoustache:"⎱",textvisiblespace:"␣",Elzdshfnc:"┆",Elzsqfnw:"┙",diagup:"╱",Elzvrecto:"▯",Elzcirfl:"◐",Elzcirfr:"◑",Elzcirfb:"◒",Elzrvbull:"◘",Elzsqfl:"◧",Elzsqfr:"◨",Elzsqfse:"◪",bigcirc:"◯",rightmoon:"☾",mercury:"☿",venus:"♀",male:"♂",jupiter:"♃",saturn:"♄",uranus:"♅",neptune:"♆",pluto:"♇",aries:"♈",taurus:"♉",gemini:"♊",cancer:"♋",leo:"♌",virgo:"♍",libra:"♎",scorpio:"♏",sagittarius:"♐",capricornus:"♑",aquarius:"♒",pisces:"♓",quarternote:"♩",eighthnote:"♪",UpArrowBar:"⤒",DownArrowBar:"⤓",Elolarr:"⥀",Elorarr:"⥁",ElzRlarr:"⥂",ElzrLarr:"⥄",Elzrarrx:"⥇",LeftRightVector:"⥎",RightUpDownVector:"⥏",DownLeftRightVector:"⥐",LeftUpDownVector:"⥑",LeftVectorBar:"⥒",RightVectorBar:"⥓",RightUpVectorBar:"⥔",RightDownVectorBar:"⥕",DownLeftVectorBar:"⥖",DownRightVectorBar:"⥗",LeftUpVectorBar:"⥘",LeftDownVectorBar:"⥙",LeftTeeVector:"⥚",RightTeeVector:"⥛",RightUpTeeVector:"⥜",RightDownTeeVector:"⥝",DownLeftTeeVector:"⥞",DownRightTeeVector:"⥟",LeftUpTeeVector:"⥠",LeftDownTeeVector:"⥡",UpEquilibrium:"⥮",ReverseUpEquilibrium:"⥯",RoundImplies:"⥰",Elztfnc:"⦀",Elroang:"⦆",Elzddfnc:"⦙",Angle:"⦜",Elzlpargt:"⦠",ElzLap:"⧊",Elzdefas:"⧋",LeftTriangleBar:"⧏",NotLeftTriangleBar:"⧏",RightTriangleBar:"⧐",NotRightTriangleBar:"⧐",RuleDelayed:"⧴",Elxuplus:"⨄",ElzThr:"⨅",Elxsqcup:"⨆",ElzInf:"⨇",ElzSup:"⨈",ElzCint:"⨍",clockoint:"⨏",sqrint:"⨖",ElzTimes:"⨯",amalg:"⨿",ElzAnd:"⩓",ElzOr:"⩔",ElOr:"⩖",Elzminhat:"⩟",Equal:"⩵",nleqslant:"⩽",geqslant:"⩾",ngeqslant:"⩾",lessapprox:"⪅",gtrapprox:"⪆",lneq:"⪇",gneq:"⪈",lnapprox:"⪉",gnapprox:"⪊",lesseqqgtr:"⪋",gtreqqless:"⪌",eqslantless:"⪕",eqslantgtr:"⪖",NestedLessLess:"⪡",NotNestedLessLess:"⪡",NestedGreaterGreater:"⪢",NotNestedGreaterGreater:"⪢",precneqq:"⪵",succneqq:"⪶",precnapprox:"⪹",succnapprox:"⪺",subseteqq:"⫅",nsubseteqq:"⫅",supseteqq:"⫆",subsetneqq:"⫋",supsetneqq:"⫌",Elztdcol:"⫶",openbracketleft:"〚",openbracketright:"〛"},t.isCharacterUnicode=n,t.characterUnicode=function(e){return n(e)?t.characterUnicodeChart[e]:void 0}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.barredLUnicodeChart={l:"ł",L:"Ł"},t.barredLUnicode=function(e){return t.barredLUnicodeChart[e]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.slashed_o="ø",t.slashed_O="Ø",t.slashedOUnicodeChart={o:t.slashed_o,O:t.slashed_O},t.slashedOUnicode=function(e){return t.slashedOUnicodeChart[e]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.cyrillicUnicodeChart={CYRF:"Ф",CYRII:"І",CYROMEGA:"Ѡ",CYRG:"Г",cyrkvcrs:"ҝ",cyryo:"ё",CYRH:"Х",CYRZHDSC:"Җ",cyrphk:"ҧ",CYRTDSC:"Ҭ",CYRI:"И",cyryi:"ї",CYRDZHE:"Џ",cyriote:"ѥ",CYRK:"К",CYRSHHA:"Һ",CYRL:"Л",CYRM:"М",CYRCHLDSC:"Ӌ",CYRNJE:"Њ",CYRYAT:"Ѣ",CYRA:"А",CYRB:"Б",cyrchrdsc:"ҷ",cyrschwa:"ә",CYRDZE:"Ѕ",CYRIE:"Є",CYRC:"Ц",CYRZH:"Ж",CYRD:"Д",CYRABHCHDSC:"Ҿ",CYRFITA:"Ѳ",CYRE:"Е",CYRABHHA:"Ҩ",cyrya:"я",cyrdzhe:"џ",CYRIOTLYUS:"Ѩ",cyrsemisftsn:"ҍ",CYRV:"В",cyrishrt:"й",cyrdje:"ђ",cyrchldsc:"ӌ",CYRY:"Ү",cyrndsc:"ң",CYRZ:"З",CYRKHCRS:"Ҟ",CYRNG:"Ҥ",CYRCHRDSC:"Ҷ",CYRYHCRS:"Ұ",CYRSHCH:"Щ",CYRUSHRT:"Ў",cyryu:"ю",cyrksi:"ѯ",CYRN:"Н",CYRO:"О",CYRBYUS:"Ѫ",CYRP:"П",CYRZDSC:"Ҙ",CYRAE:"Ӕ",CYRR:"Р",CYRS:"С",CYRT:"Т",CYRABHCH:"Ҽ",cyruk:"ѹ",CYRU:"У",cyrii:"і",CYRSEMISFTSN:"Ҍ",cyrghcrs:"ғ",CYRISHRT:"Й",cyromegatitlo:"ѽ",cyrkbeak:"ҡ",cyrie:"є",cyrzdsc:"ҙ",CYRNDSC:"Ң",CYRGUP:"Ґ",cyrshch:"щ",CYRKHK:"Ӄ",cyrzh:"ж",CYRJE:"Ј",cyrthousands:"҂",cyrabhch:"ҽ",textnumero:"№",cyrng:"ҥ",CYRPSI:"Ѱ",CYRTETSE:"Ҵ",CYRIOTBYUS:"Ѭ",cyrnje:"њ",CYRIOTE:"Ѥ",cyrdze:"ѕ",cyrae:"ӕ",CYRHRDSN:"Ъ",CYRKOPPA:"Ҁ",CYRRTICK:"Ҏ",CYRSCHWA:"Ә",cyrtdsc:"ҭ",CYRGHK:"Ҕ",cyrabhha:"ҩ",cyrshha:"һ",CYRSH:"Ш",cyru:"у",cyrkhcrs:"ҟ",cyrt:"т",CYRERY:"Ы",cyrs:"с",cyrr:"р",CYROT:"Ѿ",cyrlyus:"ѧ",CYRNHK:"Ӈ",CYRSFTSN:"Ь",cyrghk:"ҕ",cyrp:"п",cyrabhdze:"ӡ",cyro:"о",CYRTSHE:"Ћ",cyrn:"н",CYRSDSC:"Ҫ",cyryhcrs:"ұ",cyrpsi:"ѱ",cyrz:"з",cyry:"ү",cyrje:"ј",cyrv:"в",cyrchvcrs:"ҹ",cyrkhk:"ӄ",cyre:"е",cyromega:"ѡ",cyrd:"д",cyrc:"ц",cyrb:"б",CYROTLD:"Ө",cyrgup:"ґ",CYRLJE:"Љ",cyra:"а",CYROMEGATITLO:"Ѽ",CYRGHCRS:"Ғ",CYRCHVCRS:"Ҹ",cyrm:"м",cyrl:"л",cyrsh:"ш",cyrk:"к",cyri:"и",cyrh:"х",CYRHDSC:"Ҳ",CYRIZH:"Ѵ",CYRABHDZE:"Ӡ",cyrkdsc:"қ",cyrg:"г",CYRCH:"Ч",cyrf:"ф",CYRYI:"Ї",cyrmillions:"҉",CYRKSI:"Ѯ",CYROMEGARND:"Ѻ",cyrot:"ѿ",cyrtetse:"ҵ",cyrhdsc:"ҳ",cyrushrt:"ў",cyriotlyus:"ѩ",CYRYA:"Я",cyrlje:"љ",cyrotld:"ө",CYRKDSC:"Қ",cyrhrdsn:"ъ",cyrrtick:"ҏ",cyrkoppa:"ҁ",CYRDJE:"Ђ",cyriotbyus:"ѭ",cyrhundredthousands:"҈",CYRpalochka:"Ӏ",CYRKVCRS:"Ҝ",cyromegarnd:"ѻ",cyrsftsn:"ь",cyrabhchdsc:"ҿ",cyrzhdsc:"җ",cyrerev:"э",CYRLYUS:"Ѧ",CYRKBEAK:"Ҡ",cyrery:"ы",CYREREV:"Э",cyrnhk:"ӈ",cyrsdsc:"ҫ",cyrch:"ч",cyrtshe:"ћ",CYRPHK:"Ҧ",CYRYO:"Ё",CYRYU:"Ю",CYRUK:"Ѹ"},t.cyrillicUnicode=function(e){return t.cyrillicUnicodeChart[e]}},function(e,t,i){"use strict";function n(e){return t.specialCharacters.hasOwnProperty(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.specialCharacters={i:"ı",j:"ȷ",oe:"œ",OE:"Œ",ae:"æ",AE:"Æ",aa:"å",AA:"Å",o:"ø",O:"Ø",ss:"ß",l:"ł",L:"Ł"},t.isSpecialCharacter=n,t.specialCharacter=function(e){return n(e)?t.specialCharacters[e]:void 0}},function(e,t,i){"use strict";function n(e,t){switch(e){case"cyrchar":var i=o.translateCharToCyrillic(t);if(i)return i;break;default:for(var n=0,r=[a.diacriticUnicode,s.formattingUnicode];n0&&(e=this.select_data[0].id),this.$el.select2("val",e)},_template:function(e){return''}});t.default={View:o}}).call(t,i(1),i(2))},function(e,t,i){"use strict";(function(e,n){function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var s=i(2),o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t.default=e,t}(s),r=i(14),l=a(r),d=i(15),c=a(d),u=i(141),h=(a(u),{hidden:!1,show:function(){this.set("hidden",!1)},hide:function(){this.set("hidden",!0)},toggle:function(){this.set("hidden",!this.get("hidden"))},is_visible:function(){return!this.attributes.hidden}}),f=e.Model.extend({defaults:{name:null,label:null,type:null,value:null,html:null,num_samples:5},initialize:function(e){this.attributes.html=unescape(this.attributes.html)},copy:function(){return new f(this.toJSON())},set_value:function(e){this.set("value",e||"")}}),p=e.Collection.extend({model:f}),m=f.extend({}),_=f.extend({set_value:function(e){this.set("value",parseInt(e,10))},get_samples:function(){return d3.scale.linear().domain([this.get("min"),this.get("max")]).ticks(this.get("num_samples"))}}),g=_.extend({set_value:function(e){this.set("value",parseFloat(e))}}),v=f.extend({get_samples:function(){return o.map(this.get("options"),function(e){return e[0]})}});f.subModelTypes={integer:_,float:g,data:m,select:v};var w=e.Model.extend({defaults:{id:null,name:null,description:null,target:null,inputs:[],outputs:[]},urlRoot:Galaxy.root+"api/tools",initialize:function(e){this.set("inputs",new p(o.map(e.inputs,function(e){return new(f.subModelTypes[e.type]||f)(e)})))},toJSON:function(){var t=e.Model.prototype.toJSON.call(this);return t.inputs=this.get("inputs").map(function(e){return e.toJSON()}),t},remove_inputs:function(e){var t=this,i=t.get("inputs").filter(function(t){return-1!==e.indexOf(t.get("type"))});t.get("inputs").remove(i)},copy:function(t){var i=new w(this.toJSON());if(t){var n=new e.Collection;i.get("inputs").each(function(e){e.get_samples()&&n.push(e)}),i.set("inputs",n)}return i},apply_search_results:function(e){return-1!==o.indexOf(e,this.attributes.id)?this.show():this.hide(),this.is_visible()},set_input_value:function(e,t){this.get("inputs").find(function(t){return t.get("name")===e}).set("value",t)},set_input_values:function(e){var t=this;o.each(o.keys(e),function(i){t.set_input_value(i,e[i])})},run:function(){return this._run()},rerun:function(e,t){return this._run({action:"rerun",target_dataset_id:e.id,regions:t})},get_inputs_dict:function(){var e={};return this.get("inputs").each(function(t){e[t.get("name")]=t.get("value")}),e},_run:function(e){var t=o.extend({tool_id:this.id,inputs:this.get_inputs_dict()},e),i=n.Deferred(),a=new l.default.ServerStateDeferred({ajax_settings:{url:this.urlRoot,data:JSON.stringify(t),dataType:"json",contentType:"application/json",type:"POST"},interval:2e3,success_fn:function(e){return"pending"!==e}});return n.when(a.go()).then(function(e){i.resolve(new c.default.DatasetCollection(e))}),i}});o.extend(w.prototype,h);var b=(e.View.extend({}),e.Collection.extend({model:w})),y=e.Model.extend(h),x=e.Model.extend({defaults:{elems:[],open:!1},clear_search_results:function(){o.each(this.attributes.elems,function(e){e.show()}),this.show(),this.set("open",!1)},apply_search_results:function(e){var t,i=!0;o.each(this.attributes.elems,function(n){n instanceof y?(t=n,t.hide()):n instanceof w&&n.apply_search_results(e)&&(i=!1,t&&t.show())}),i?this.hide():(this.show(),this.set("open",!0))}});o.extend(x.prototype,h);var k=e.Model.extend({defaults:{search_hint_string:"search tools",min_chars_for_search:3,clear_btn_url:"",visible:!0,query:"",results:null,clear_key:27},urlRoot:Galaxy.root+"api/tools",initialize:function(){this.on("change:query",this.do_search)},do_search:function(){var e=this.attributes.query;if(e.length");e.append(A.tool_link(this.model.toJSON()));var t=this.model.get("form_style",null);if("upload1"===this.model.id)e.find("a").on("click",function(e){e.preventDefault(),Galaxy.upload.show()});else if("regular"===t){var i=this;e.find("a").on("click",function(e){e.preventDefault(),Galaxy.router.push("/",{tool_id:i.model.id,version:i.model.get("version")})})}return this.$el.append(e),this}}),T=$.extend({tagName:"div",className:"toolPanelLabel",render:function(){return this.$el.append(n("").text(this.model.attributes.text)),this}}),M=$.extend({tagName:"div",className:"toolSectionWrapper",initialize:function(){$.prototype.initialize.call(this),this.model.on("change:open",this.update_open,this)},render:function(){this.$el.append(A.panel_section(this.model.toJSON()));var e=this.$el.find(".toolSectionBody");return o.each(this.model.attributes.elems,function(t){if(t instanceof w){var i=new S({model:t,className:"toolTitle"});i.render(),e.append(i.$el)}else if(t instanceof y){var n=new T({model:t});n.render(),e.append(n.$el)}}),this},events:{"click .toolSectionTitle > a":"toggle"},toggle:function(){this.model.set("open",!this.model.attributes.open)},update_open:function(){this.model.attributes.open?this.$el.children(".toolSectionBody").slideDown("fast"):this.$el.children(".toolSectionBody").slideUp("fast")}}),E=e.View.extend({tagName:"div",id:"tool-search",className:"bar",events:{click:"focus_and_select","keyup :input":"query_changed","change :input":"query_changed","click #search-clear-btn":"clear"},render:function(){return this.$el.append(A.tool_search(this.model.toJSON())),this.model.is_visible()||this.$el.hide(),n("#messagebox").is(":visible")&&this.$el.css("top","95px"),this.$el.find("[title]").tooltip(),this},focus_and_select:function(){this.$el.find(":input").focus().select()},clear:function(){return this.model.clear_search(),this.$el.find(":input").val(""),this.focus_and_select(),!1},query_changed:function(e){if(this.model.attributes.clear_key&&this.model.attributes.clear_key===e.which)return this.clear(),!1;this.model.set("query",this.$el.find(":input").val())}}),P=e.View.extend({tagName:"div",className:"toolMenu",initialize:function(){this.model.get("tool_search").on("change:results",this.handle_search_results,this)},render:function(){var e=this,t=new E({model:this.model.get("tool_search")});return t.render(),e.$el.append(t.$el),this.model.get("layout").each(function(t){if(t instanceof x){var i=new M({model:t});i.render(),e.$el.append(i.$el)}else if(t instanceof w){var n=new S({model:t,className:"toolTitleNoSection"});n.render(),e.$el.append(n.$el)}else if(t instanceof y){var a=new T({model:t});a.render(),e.$el.append(a.$el)}}),e.$el.find("a.tool-link").click(function(t){var i=n(this).attr("class").split(/\s+/)[0],a=e.model.get("tools").get(i);e.trigger("tool_link_click",t,a)}),this},handle_search_results:function(){var e=this.model.get("tool_search").get("results");e&&0===e.length?n("#search-no-results").show():n("#search-no-results").hide()}}),O=e.View.extend({className:"toolForm",render:function(){this.$el.children().remove(),this.$el.append(A.tool_form(this.model.toJSON()))}}),A=(e.View.extend({className:"toolMenuAndView",initialize:function(){this.tool_panel_view=new P({collection:this.collection}),this.tool_form_view=new O},render:function(){this.tool_panel_view.render(),this.tool_panel_view.$el.css("float","left"),this.$el.append(this.tool_panel_view.$el),this.tool_form_view.$el.hide(),this.$el.append(this.tool_form_view.$el);var e=this;this.tool_panel_view.on("tool_link_click",function(t,i){t.preventDefault(),e.show_tool(i)})},show_tool:function(e){var t=this;e.fetch().done(function(){t.tool_form_view.model=e,t.tool_form_view.render(),t.tool_form_view.$el.show(),n("#left").width("650px")})}}),{tool_search:o.template(['',' ',''].join("")),panel_section:o.template(['",'