@girder/core 5.0.0-beta.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/dist-lib/Girder_Favicon.png +0 -0
  2. package/dist-lib/Girder_Mark.png +0 -0
  3. package/dist-lib/girder-core.css +2 -0
  4. package/dist-lib/girder-core.js +60421 -0
  5. package/dist-lib/girder-core.js.map +1 -0
  6. package/dist-lib/girder-core.umd.cjs +1484 -0
  7. package/dist-lib/girder-core.umd.cjs.map +1 -0
  8. package/dist-lib/index.d.ts +1 -0
  9. package/dist-lib/src/auth.js +112 -0
  10. package/dist-lib/src/collections/ApiKeyCollection.js +9 -0
  11. package/dist-lib/src/collections/AssetstoreCollection.js +10 -0
  12. package/dist-lib/src/collections/Collection.js +295 -0
  13. package/dist-lib/src/collections/CollectionCollection.js +9 -0
  14. package/dist-lib/src/collections/FileCollection.js +11 -0
  15. package/dist-lib/src/collections/FolderCollection.js +11 -0
  16. package/dist-lib/src/collections/GroupCollection.js +9 -0
  17. package/dist-lib/src/collections/ItemCollection.js +11 -0
  18. package/dist-lib/src/collections/UserCollection.js +24 -0
  19. package/dist-lib/src/collections/index.js +21 -0
  20. package/dist-lib/src/constants.js +32 -0
  21. package/dist-lib/src/dialog.js +128 -0
  22. package/dist-lib/src/events.js +6 -0
  23. package/dist-lib/src/index.ts +80 -0
  24. package/dist-lib/src/main.ts +61 -0
  25. package/dist-lib/src/misc.js +260 -0
  26. package/dist-lib/src/models/AccessControlledModel.js +76 -0
  27. package/dist-lib/src/models/ApiKeyModel.js +31 -0
  28. package/dist-lib/src/models/AssetstoreModel.js +43 -0
  29. package/dist-lib/src/models/CollectionCreationPolicyModel.js +20 -0
  30. package/dist-lib/src/models/CollectionModel.js +12 -0
  31. package/dist-lib/src/models/FileModel.js +310 -0
  32. package/dist-lib/src/models/FolderModel.js +33 -0
  33. package/dist-lib/src/models/GroupModel.js +197 -0
  34. package/dist-lib/src/models/ItemModel.js +72 -0
  35. package/dist-lib/src/models/MetadataMixin.js +88 -0
  36. package/dist-lib/src/models/Model.js +187 -0
  37. package/dist-lib/src/models/UserModel.js +189 -0
  38. package/dist-lib/src/models/index.js +25 -0
  39. package/dist-lib/src/pluginUtils.js +11 -0
  40. package/dist-lib/src/rest.js +221 -0
  41. package/dist-lib/src/router.js +58 -0
  42. package/dist-lib/src/routes.js +229 -0
  43. package/dist-lib/src/stylesheets/apidocs/apidocs.styl +50 -0
  44. package/dist-lib/src/stylesheets/body/adminConsole.styl +21 -0
  45. package/dist-lib/src/stylesheets/body/assetstores.styl +46 -0
  46. package/dist-lib/src/stylesheets/body/collectionList.styl +39 -0
  47. package/dist-lib/src/stylesheets/body/collectionPage.styl +6 -0
  48. package/dist-lib/src/stylesheets/body/frontPage.styl +48 -0
  49. package/dist-lib/src/stylesheets/body/groupList.styl +43 -0
  50. package/dist-lib/src/stylesheets/body/groupPage.styl +116 -0
  51. package/dist-lib/src/stylesheets/body/itemPage.styl +81 -0
  52. package/dist-lib/src/stylesheets/body/plugins.styl +61 -0
  53. package/dist-lib/src/stylesheets/body/searchResultsList.styl +51 -0
  54. package/dist-lib/src/stylesheets/body/systemConfig.styl +56 -0
  55. package/dist-lib/src/stylesheets/body/userAccount.styl +57 -0
  56. package/dist-lib/src/stylesheets/body/userList.styl +79 -0
  57. package/dist-lib/src/stylesheets/body/userPage.styl +6 -0
  58. package/dist-lib/src/stylesheets/layout/footer.styl +19 -0
  59. package/dist-lib/src/stylesheets/layout/global.styl +154 -0
  60. package/dist-lib/src/stylesheets/layout/globalNav.styl +89 -0
  61. package/dist-lib/src/stylesheets/layout/header.styl +29 -0
  62. package/dist-lib/src/stylesheets/layout/headerUser.styl +33 -0
  63. package/dist-lib/src/stylesheets/layout/layout.styl +75 -0
  64. package/dist-lib/src/stylesheets/layout/layoutVars.styl +9 -0
  65. package/dist-lib/src/stylesheets/layout/loading.styl +37 -0
  66. package/dist-lib/src/stylesheets/layout/progressArea.styl +17 -0
  67. package/dist-lib/src/stylesheets/widgets/accessWidget.styl +106 -0
  68. package/dist-lib/src/stylesheets/widgets/browserWidget.styl +9 -0
  69. package/dist-lib/src/stylesheets/widgets/hierarchyWidget.styl +188 -0
  70. package/dist-lib/src/stylesheets/widgets/markdownWidget.styl +92 -0
  71. package/dist-lib/src/stylesheets/widgets/metadataWidget.styl +92 -0
  72. package/dist-lib/src/stylesheets/widgets/searchFieldWidget.styl +70 -0
  73. package/dist-lib/src/stylesheets/widgets/taskProgress.styl +41 -0
  74. package/dist-lib/src/stylesheets/widgets/timelineWidget.styl +41 -0
  75. package/dist-lib/src/stylesheets/widgets/uploadWidget.styl +43 -0
  76. package/dist-lib/src/stylesheets/widgets/userOtpManagementWidget.styl +159 -0
  77. package/dist-lib/src/templates/body/adminConsole.pug +13 -0
  78. package/dist-lib/src/templates/body/assetstores.pug +83 -0
  79. package/dist-lib/src/templates/body/collectionList.pug +40 -0
  80. package/dist-lib/src/templates/body/collectionPage.pug +36 -0
  81. package/dist-lib/src/templates/body/filesystemImport.pug +41 -0
  82. package/dist-lib/src/templates/body/frontPage.pug +83 -0
  83. package/dist-lib/src/templates/body/groupList.pug +30 -0
  84. package/dist-lib/src/templates/body/groupPage.pug +116 -0
  85. package/dist-lib/src/templates/body/itemPage.pug +61 -0
  86. package/dist-lib/src/templates/body/plugins.pug +20 -0
  87. package/dist-lib/src/templates/body/s3Import.pug +35 -0
  88. package/dist-lib/src/templates/body/searchResults.pug +15 -0
  89. package/dist-lib/src/templates/body/searchResultsType.pug +13 -0
  90. package/dist-lib/src/templates/body/systemConfiguration.pug +215 -0
  91. package/dist-lib/src/templates/body/userAccount.pug +83 -0
  92. package/dist-lib/src/templates/body/userList.pug +43 -0
  93. package/dist-lib/src/templates/body/userPage.pug +40 -0
  94. package/dist-lib/src/templates/layout/alert.pug +5 -0
  95. package/dist-lib/src/templates/layout/layout.pug +12 -0
  96. package/dist-lib/src/templates/layout/layoutFooter.pug +11 -0
  97. package/dist-lib/src/templates/layout/layoutGlobalNav.pug +7 -0
  98. package/dist-lib/src/templates/layout/layoutHeader.pug +8 -0
  99. package/dist-lib/src/templates/layout/layoutHeaderUser.pug +26 -0
  100. package/dist-lib/src/templates/layout/layoutProgressArea.pug +1 -0
  101. package/dist-lib/src/templates/layout/loginDialog.pug +30 -0
  102. package/dist-lib/src/templates/layout/registerDialog.pug +35 -0
  103. package/dist-lib/src/templates/layout/resetPasswordDialog.pug +25 -0
  104. package/dist-lib/src/templates/widgets/accessEditor.pug +23 -0
  105. package/dist-lib/src/templates/widgets/accessEditorMixins.pug +57 -0
  106. package/dist-lib/src/templates/widgets/accessEditorNonModal.pug +11 -0
  107. package/dist-lib/src/templates/widgets/accessEntry.pug +32 -0
  108. package/dist-lib/src/templates/widgets/apiKeyList.pug +50 -0
  109. package/dist-lib/src/templates/widgets/browserWidget.pug +32 -0
  110. package/dist-lib/src/templates/widgets/checkedActionsMenu.pug +46 -0
  111. package/dist-lib/src/templates/widgets/collectionInfoDialog.pug +37 -0
  112. package/dist-lib/src/templates/widgets/confirmDialog.pug +14 -0
  113. package/dist-lib/src/templates/widgets/dateTimeRangeWidget.pug +20 -0
  114. package/dist-lib/src/templates/widgets/dateTimeWidget.pug +8 -0
  115. package/dist-lib/src/templates/widgets/editApiKeyWidget.pug +43 -0
  116. package/dist-lib/src/templates/widgets/editAssetstoreWidget.pug +70 -0
  117. package/dist-lib/src/templates/widgets/editCollectionWidget.pug +27 -0
  118. package/dist-lib/src/templates/widgets/editFileWidget.pug +21 -0
  119. package/dist-lib/src/templates/widgets/editFolderWidget.pug +27 -0
  120. package/dist-lib/src/templates/widgets/editGroupWidget.pug +54 -0
  121. package/dist-lib/src/templates/widgets/editItemWidget.pug +27 -0
  122. package/dist-lib/src/templates/widgets/fileInfoDialog.pug +33 -0
  123. package/dist-lib/src/templates/widgets/fileList.pug +33 -0
  124. package/dist-lib/src/templates/widgets/folderInfoDialog.pug +42 -0
  125. package/dist-lib/src/templates/widgets/folderList.pug +21 -0
  126. package/dist-lib/src/templates/widgets/groupAdminList.pug +33 -0
  127. package/dist-lib/src/templates/widgets/groupInviteDialog.pug +76 -0
  128. package/dist-lib/src/templates/widgets/groupInviteList.pug +14 -0
  129. package/dist-lib/src/templates/widgets/groupMemberList.pug +39 -0
  130. package/dist-lib/src/templates/widgets/groupModList.pug +23 -0
  131. package/dist-lib/src/templates/widgets/hierarchyBreadcrumb.pug +37 -0
  132. package/dist-lib/src/templates/widgets/hierarchyPaginated.pug +19 -0
  133. package/dist-lib/src/templates/widgets/hierarchyWidget.pug +96 -0
  134. package/dist-lib/src/templates/widgets/itemBreadcrumb.pug +16 -0
  135. package/dist-lib/src/templates/widgets/itemList.pug +27 -0
  136. package/dist-lib/src/templates/widgets/jsonMetadatumEditWidget.pug +13 -0
  137. package/dist-lib/src/templates/widgets/jsonMetadatumView.pug +6 -0
  138. package/dist-lib/src/templates/widgets/loadingAnimation.pug +4 -0
  139. package/dist-lib/src/templates/widgets/markdownWidget.pug +34 -0
  140. package/dist-lib/src/templates/widgets/metadataWidget.pug +16 -0
  141. package/dist-lib/src/templates/widgets/metadatumEditWidget.pug +15 -0
  142. package/dist-lib/src/templates/widgets/metadatumView.pug +5 -0
  143. package/dist-lib/src/templates/widgets/newAssetstore.pug +87 -0
  144. package/dist-lib/src/templates/widgets/paginateWidget.pug +7 -0
  145. package/dist-lib/src/templates/widgets/pluginConfigBreadcrumb.pug +13 -0
  146. package/dist-lib/src/templates/widgets/rootSelectorWidget.pug +13 -0
  147. package/dist-lib/src/templates/widgets/searchField.pug +10 -0
  148. package/dist-lib/src/templates/widgets/searchHelp.pug +12 -0
  149. package/dist-lib/src/templates/widgets/searchModeSelect.pug +9 -0
  150. package/dist-lib/src/templates/widgets/searchResults.pug +14 -0
  151. package/dist-lib/src/templates/widgets/sortCollectionWidget.pug +14 -0
  152. package/dist-lib/src/templates/widgets/taskProgress.pug +16 -0
  153. package/dist-lib/src/templates/widgets/timeline.pug +15 -0
  154. package/dist-lib/src/templates/widgets/uploadWidget.pug +15 -0
  155. package/dist-lib/src/templates/widgets/uploadWidgetMixins.pug +31 -0
  156. package/dist-lib/src/templates/widgets/uploadWidgetNonModal.pug +10 -0
  157. package/dist-lib/src/templates/widgets/userOtpBegin.pug +4 -0
  158. package/dist-lib/src/templates/widgets/userOtpDisable.pug +6 -0
  159. package/dist-lib/src/templates/widgets/userOtpEnable.pug +44 -0
  160. package/dist-lib/src/utilities/EventStream.js +119 -0
  161. package/dist-lib/src/utilities/PluginUtils.js +36 -0
  162. package/dist-lib/src/utilities/S3UploadHandler.js +263 -0
  163. package/dist-lib/src/utilities/index.js +9 -0
  164. package/dist-lib/src/utilities/jquery/girderEnable.js +19 -0
  165. package/dist-lib/src/utilities/jquery/girderModal.js +48 -0
  166. package/dist-lib/src/version.js +6 -0
  167. package/dist-lib/src/views/App.js +359 -0
  168. package/dist-lib/src/views/View.js +79 -0
  169. package/dist-lib/src/views/body/AdminView.js +29 -0
  170. package/dist-lib/src/views/body/AssetstoresView.js +233 -0
  171. package/dist-lib/src/views/body/CollectionView.js +188 -0
  172. package/dist-lib/src/views/body/CollectionsView.js +120 -0
  173. package/dist-lib/src/views/body/FilesystemImportView.js +83 -0
  174. package/dist-lib/src/views/body/FolderView.js +54 -0
  175. package/dist-lib/src/views/body/FrontPageView.js +47 -0
  176. package/dist-lib/src/views/body/GroupView.js +336 -0
  177. package/dist-lib/src/views/body/GroupsView.js +106 -0
  178. package/dist-lib/src/views/body/ItemView.js +177 -0
  179. package/dist-lib/src/views/body/PluginsView.js +73 -0
  180. package/dist-lib/src/views/body/S3ImportView.js +80 -0
  181. package/dist-lib/src/views/body/SearchResultsView.js +162 -0
  182. package/dist-lib/src/views/body/SystemConfigurationView.js +177 -0
  183. package/dist-lib/src/views/body/UserAccountView.js +179 -0
  184. package/dist-lib/src/views/body/UserView.js +165 -0
  185. package/dist-lib/src/views/body/UsersView.js +124 -0
  186. package/dist-lib/src/views/body/index.js +38 -0
  187. package/dist-lib/src/views/index.js +13 -0
  188. package/dist-lib/src/views/layout/FooterView.js +29 -0
  189. package/dist-lib/src/views/layout/GlobalNavView.js +103 -0
  190. package/dist-lib/src/views/layout/HeaderUserView.js +45 -0
  191. package/dist-lib/src/views/layout/HeaderView.js +83 -0
  192. package/dist-lib/src/views/layout/LoginView.js +100 -0
  193. package/dist-lib/src/views/layout/ProgressListView.js +70 -0
  194. package/dist-lib/src/views/layout/RegisterView.js +101 -0
  195. package/dist-lib/src/views/layout/ResetPasswordView.js +70 -0
  196. package/dist-lib/src/views/layout/index.js +19 -0
  197. package/dist-lib/src/views/widgets/AccessWidget.js +427 -0
  198. package/dist-lib/src/views/widgets/ApiKeyListWidget.js +140 -0
  199. package/dist-lib/src/views/widgets/BrowserWidget.js +317 -0
  200. package/dist-lib/src/views/widgets/CheckedMenuWidget.js +68 -0
  201. package/dist-lib/src/views/widgets/CollectionInfoWidget.js +40 -0
  202. package/dist-lib/src/views/widgets/DateTimeRangeWidget.js +180 -0
  203. package/dist-lib/src/views/widgets/DateTimeWidget.js +110 -0
  204. package/dist-lib/src/views/widgets/EditApiKeyWidget.js +124 -0
  205. package/dist-lib/src/views/widgets/EditAssetstoreWidget.js +136 -0
  206. package/dist-lib/src/views/widgets/EditCollectionWidget.js +93 -0
  207. package/dist-lib/src/views/widgets/EditFileWidget.js +56 -0
  208. package/dist-lib/src/views/widgets/EditFolderWidget.js +116 -0
  209. package/dist-lib/src/views/widgets/EditGroupWidget.js +125 -0
  210. package/dist-lib/src/views/widgets/EditItemWidget.js +110 -0
  211. package/dist-lib/src/views/widgets/FileInfoWidget.js +26 -0
  212. package/dist-lib/src/views/widgets/FileListWidget.js +151 -0
  213. package/dist-lib/src/views/widgets/FolderInfoWidget.js +39 -0
  214. package/dist-lib/src/views/widgets/FolderListWidget.js +106 -0
  215. package/dist-lib/src/views/widgets/GroupAdminsWidget.js +88 -0
  216. package/dist-lib/src/views/widgets/GroupInvitesWidget.js +56 -0
  217. package/dist-lib/src/views/widgets/GroupMembersWidget.js +185 -0
  218. package/dist-lib/src/views/widgets/GroupModsWidget.js +77 -0
  219. package/dist-lib/src/views/widgets/HierarchyWidget.js +1097 -0
  220. package/dist-lib/src/views/widgets/ItemBreadcrumbWidget.js +38 -0
  221. package/dist-lib/src/views/widgets/ItemListWidget.js +345 -0
  222. package/dist-lib/src/views/widgets/LoadingAnimation.js +17 -0
  223. package/dist-lib/src/views/widgets/MarkdownWidget.js +217 -0
  224. package/dist-lib/src/views/widgets/MetadataWidget.js +508 -0
  225. package/dist-lib/src/views/widgets/NewAssetstoreWidget.js +72 -0
  226. package/dist-lib/src/views/widgets/PaginateWidget.js +37 -0
  227. package/dist-lib/src/views/widgets/PluginConfigBreadcrumbWidget.js +33 -0
  228. package/dist-lib/src/views/widgets/RootSelectorWidget.js +192 -0
  229. package/dist-lib/src/views/widgets/SearchFieldWidget.js +365 -0
  230. package/dist-lib/src/views/widgets/SearchPaginateWidget.js +149 -0
  231. package/dist-lib/src/views/widgets/SortCollectionWidget.js +53 -0
  232. package/dist-lib/src/views/widgets/TaskProgressWidget.js +91 -0
  233. package/dist-lib/src/views/widgets/TimelineWidget.js +155 -0
  234. package/dist-lib/src/views/widgets/UploadWidget.js +340 -0
  235. package/dist-lib/src/views/widgets/UserOtpManagementWidget.js +105 -0
  236. package/dist-lib/src/views/widgets/index.js +81 -0
  237. package/dist-lib/src/vite-env.d.ts +5 -0
  238. package/package.json +17 -15
@@ -0,0 +1,192 @@
1
+ import _ from 'underscore';
2
+
3
+ import CollectionCollection from '@girder/core/collections/CollectionCollection';
4
+ import UserCollection from '@girder/core/collections/UserCollection';
5
+ import View from '@girder/core/views/View';
6
+ import events from '@girder/core/events';
7
+ import { getCurrentUser } from '@girder/core/auth';
8
+
9
+ import RootSelectorWidgetTemplate from '@girder/core/templates/widgets/rootSelectorWidget.pug';
10
+
11
+ /**
12
+ * This widget creates a dropdown box allowing the user to select
13
+ * "root" paths for a hierarchy widget.
14
+ *
15
+ * @emits RootSelectorWidget#g:selected The selected element changed
16
+ * @type {object}
17
+ * @property {Model|null} root The selected root model
18
+ * @property {string|null} group The selection group
19
+ *
20
+ * @emits RootSelectorWidget#g:group A collection group was updated
21
+ * @type {object}
22
+ * @property {Collection} collection
23
+ */
24
+ var RootSelectorWidget = View.extend({
25
+ events: {
26
+ 'change #g-root-selector': '_selectRoot'
27
+ },
28
+
29
+ /**
30
+ * Initialize the widget. The caller can configure the list of items
31
+ * that are present in the select box. If no values are given, then
32
+ * appropriate rest calls will be made to fetch models automatically.
33
+ *
34
+ * Additional categories of roots can be provided via an object mapping,
35
+ * for example:
36
+ *
37
+ * {
38
+ * friends: new UserCollection([...]),
39
+ * saved: new FolderCollection([...])
40
+ * }
41
+ *
42
+ * Only a single page of results are displayed for each collection provided.
43
+ * The default maximum number can be configured, but for more sophisticated
44
+ * behavior, the caller should act on the collection objects directly and
45
+ * rerender.
46
+ *
47
+ * @param {object} settings
48
+ * @param {UserModel} [settings.home=getCurrentUser()]
49
+ * @param {object} [settings.groups] Additional collection groups to add
50
+ * @param {number} [settings.pageLimit=25] The maximum number of models to fetch
51
+ * @param {Model} [settings.selected] The default/current selection
52
+ * @param {string[]} [settings.display=['Home', 'Collections', 'Users'] Display order
53
+ * @param {boolean} [settings.reset=true] Always fetch from offset 0
54
+ * @param {Model|string} [settings.selectByResource] use the baseParentId to
55
+ * set the default selected item, can use a model based like an object, or a specific string ParentId
56
+ *
57
+ */
58
+ initialize: function (settings) {
59
+ settings = settings || {};
60
+
61
+ this.pageLimit = settings.pageLimit || 25;
62
+
63
+ // collections are provided for public access here
64
+ this.groups = {
65
+ Collections: new CollectionCollection(),
66
+ Users: new UserCollection()
67
+ };
68
+
69
+ this.groups.Collections.pageLimit = settings.pageLimit;
70
+ this.groups.Users.pageLimit = settings.pageLimit;
71
+ this.groups.Users.sortField = 'login';
72
+
73
+ // override default selection groups
74
+ _.extend(this.groups, settings.groups);
75
+
76
+ // attach collection change events
77
+ _.each(this.groups, (group) => {
78
+ this.listenTo(group, 'g:changed', () => {
79
+ this._updateGroup(group);
80
+ });
81
+ });
82
+
83
+ // possible values that determine rendered behavior for "Home":
84
+ // - model: show this model as Home
85
+ // - undefined|null: use getCurrentUser()
86
+ this.home = settings.home;
87
+
88
+ // we need to fetch the collections and rerender on login
89
+ this.listenTo(events, 'g:login', this.fetch);
90
+
91
+ this.selected = settings.selected;
92
+ this.selectByResource = settings.selectByResource;
93
+ this.display = settings.display || ['Home', 'Collections', 'Users'];
94
+
95
+ this.fetch();
96
+ },
97
+
98
+ render: function () {
99
+ this._home = this.home || getCurrentUser();
100
+ // Set the selected item if it is already defined and get it by resource if not
101
+ if (this.selectByResource) {
102
+ this.setRootByBaseParent(this.selectByResource);
103
+ }
104
+ this.$el.html(
105
+ RootSelectorWidgetTemplate({
106
+ home: this._home,
107
+ groups: this.groups,
108
+ display: this.display,
109
+ selected: this.selected,
110
+ format: this._formatName
111
+ })
112
+ );
113
+
114
+ return this;
115
+ },
116
+
117
+ setRootByBaseParent: function (baseParentId) {
118
+ // Filter for an Id String from an object if given
119
+ if (_.isObject(baseParentId)) {
120
+ if ((baseParentId.attributes || {}).baseParentId !== undefined) {
121
+ baseParentId = baseParentId.attributes.baseParentId;
122
+ }
123
+ }
124
+
125
+ if (_.isString(baseParentId)) {
126
+ _.each(this.groups, (group) => {
127
+ if (group.get(baseParentId) !== undefined) {
128
+ this.selected = group.get(baseParentId);
129
+ }
130
+ });
131
+ }
132
+ },
133
+
134
+ /**
135
+ * Called when the user selects a new item. Resolves the
136
+ * model object from the DOM and triggers the g:selected event.
137
+ */
138
+ _selectRoot: function () {
139
+ var sel = this.$(':selected');
140
+ var id = sel.val();
141
+ var group = sel.data('group') || null;
142
+ this.selected = null;
143
+ if (_.has(this.groups, group)) {
144
+ this.selected = this.groups[group].get(id);
145
+ } else if (this._home && this._home.id === id) {
146
+ this.selected = this._home;
147
+ }
148
+ this.trigger('g:selected', {
149
+ root: this.selected,
150
+ group: group
151
+ });
152
+ },
153
+
154
+ /**
155
+ * Called when a collection is modified... rerenders the view.
156
+ */
157
+ _updateGroup: function (collection) {
158
+ this.trigger('g:group', {
159
+ collection: collection
160
+ });
161
+ this.render();
162
+ },
163
+
164
+ /**
165
+ * Return a string to display for the given model.
166
+ */
167
+ _formatName: function (model) {
168
+ var name = model.id;
169
+ switch (model.get('_modelType')) {
170
+ case 'user':
171
+ name = model.get('login');
172
+ break;
173
+ case 'folder':
174
+ case 'collection':
175
+ name = model.get('name');
176
+ break;
177
+ }
178
+ return name;
179
+ },
180
+
181
+ /**
182
+ * Fetch all collections from the server.
183
+ */
184
+ fetch: function () {
185
+ var reset = this.reset;
186
+ _.each(this.groups, function (collection) {
187
+ collection.fetch(null, reset);
188
+ });
189
+ }
190
+ });
191
+
192
+ export default RootSelectorWidget;
@@ -0,0 +1,365 @@
1
+ import $ from 'jquery';
2
+ import _ from 'underscore';
3
+ // Bootstrap tooltip is required by popover
4
+ import 'bootstrap/js/tooltip';
5
+ import 'bootstrap/js/popover';
6
+
7
+ import View from '@girder/core/views/View';
8
+ import { restRequest } from '@girder/core/rest';
9
+ import router from '@girder/core/router';
10
+
11
+ import SearchFieldTemplate from '@girder/core/templates/widgets/searchField.pug';
12
+ import SearchHelpTemplate from '@girder/core/templates/widgets/searchHelp.pug';
13
+ import SearchModeSelectTemplate from '@girder/core/templates/widgets/searchModeSelect.pug';
14
+ import SearchResultsTemplate from '@girder/core/templates/widgets/searchResults.pug';
15
+ import '@girder/core/stylesheets/widgets/searchFieldWidget.styl';
16
+
17
+ /**
18
+ * This widget provides a text field that will search any set of data types
19
+ * and show matching results as the user types. Results can be clicked,
20
+ * triggering a callback.
21
+ */
22
+ var SearchFieldWidget = View.extend({
23
+ events: {
24
+ 'input .g-search-field': 'search',
25
+
26
+ 'click .g-search-mode-radio': function (e) {
27
+ this.currentMode = $(e.target).val();
28
+ this.hideResults().search();
29
+
30
+ window.setTimeout(() => {
31
+ this.$('.g-search-mode-choose').popover('hide');
32
+ }, 250);
33
+ },
34
+
35
+ 'click .g-search-result>a': function (e) {
36
+ this._resultClicked($(e.currentTarget));
37
+ },
38
+
39
+ 'keydown .g-search-field': function (e) {
40
+ var code = e.keyCode || e.which;
41
+ var list, pos;
42
+ if (code === 13 && this.noResourceSelected) { /* enter without resource selected */
43
+ e.preventDefault();
44
+ if (this.$('.g-search-field').val() !== '' && !this.noResultsPage) {
45
+ this._goToResultPage(this.$('.g-search-field').val(), this.currentMode);
46
+ }
47
+ } else if (code === 40 || code === 38) {
48
+ this.noResourceSelected = false;
49
+ if (code === 40) { /* down arrow */
50
+ list = this.$('.g-search-result');
51
+ pos = list.index(list.filter('.g-search-selected')) + 1;
52
+ list.removeClass('g-search-selected');
53
+ if (pos < list.length) {
54
+ list.eq(pos).addClass('g-search-selected');
55
+ }
56
+ if (pos === list.length) {
57
+ this.noResourceSelected = true;
58
+ }
59
+ } else if (code === 38) { /* up arrow */
60
+ list = this.$('.g-search-result');
61
+ pos = list.index(list.filter('.g-search-selected')) - 1;
62
+ list.removeClass('g-search-selected');
63
+ if (pos === -1) {
64
+ this.noResourceSelected = true;
65
+ }
66
+ if (pos === -2) {
67
+ pos = list.length - 1;
68
+ }
69
+ if (pos >= 0) {
70
+ list.eq(pos).addClass('g-search-selected');
71
+ }
72
+ }
73
+ } else if (code === 13) { /* enter with resource selected */
74
+ e.preventDefault();
75
+ this.noResourceSelected = true;
76
+ var link = this.$('.g-search-result.g-search-selected>a');
77
+ if (link.length) {
78
+ this._resultClicked(link);
79
+ }
80
+ }
81
+ }
82
+ },
83
+
84
+ /**
85
+ * @param [settings.placeholder="Search..."] The placeholder text for the input field.
86
+ * @param [settings.getInfoCallback] For custom resource types, this callback can
87
+ * be passed in to resolve their title and icon. This callback should
88
+ * return an object with "icon" and "text" fields if it can resolve
89
+ * the result, or return falsy otherwise.
90
+ * @param [settings.modes=["text", "prefix"]] A string or list of strings
91
+ * representing the allowed search modes. Supported modes: "text", "prefix".
92
+ * If multiple are allowed, users are able to select which one to use
93
+ * via a dropdown.
94
+ * @param [settings.noResultsPage=false] If truthy, don't jump to a results
95
+ * page if enter is typed with a list of search results.
96
+ */
97
+ initialize: function (settings) {
98
+ this.ajaxLock = false;
99
+ this.pending = null;
100
+ this.noResourceSelected = true;
101
+ this.placeholder = settings.placeholder || 'Search...';
102
+ this.noResultsPage = settings.noResultsPage || false;
103
+ this.getInfoCallback = settings.getInfoCallback || null;
104
+ /* The order of settings.types give the order of the display of the elements :
105
+ * ['collection', 'folder', 'item'] will be render like this
106
+ * [icon-collection] Collections..
107
+ * [icon-folder] Folders..
108
+ * [icon-item] Items..
109
+ */
110
+ this.types = settings.types || [];
111
+ this.modes = settings.modes || SearchFieldWidget.getModes();
112
+
113
+ if (!_.isArray(this.modes)) {
114
+ this.modes = [this.modes];
115
+ }
116
+
117
+ this.currentMode = this.modes[0];
118
+
119
+ // Do not change the icon for fast searches, to prevent jitter
120
+ this._animatePending = _.debounce(this._animatePending, 100);
121
+ },
122
+
123
+ search: function () {
124
+ var q = this.$('.g-search-field').val();
125
+
126
+ if (!q) {
127
+ this.hideResults();
128
+ return this;
129
+ }
130
+
131
+ if (this.ajaxLock) {
132
+ this.pending = q;
133
+ } else {
134
+ this._doSearch(q);
135
+ }
136
+
137
+ return this;
138
+ },
139
+
140
+ _goToResultPage: function (query, mode) {
141
+ this.resetState();
142
+ router.navigate(`#search/results?query=${query}&mode=${mode}`, { trigger: true });
143
+ },
144
+
145
+ _resultClicked: function (link) {
146
+ if (link.data('resourceType') === 'resultPage') {
147
+ this._goToResultPage(this.$('.g-search-field').val(), this.currentMode);
148
+ } else {
149
+ this.trigger('g:resultClicked', {
150
+ type: link.data('resourceType'),
151
+ id: link.data('resourceId'),
152
+ text: link.text().trim(),
153
+ icon: link.data('resourceIcon')
154
+ });
155
+ }
156
+ },
157
+
158
+ render: function () {
159
+ this.$el.html(SearchFieldTemplate({
160
+ placeholder: this.placeholder,
161
+ modes: this.modes,
162
+ currentMode: this.currentMode
163
+ }));
164
+
165
+ this.$('.g-search-options-button').popover({
166
+ trigger: 'manual',
167
+ viewport: {
168
+ selector: 'body',
169
+ padding: 10
170
+ },
171
+ content: () => {
172
+ return SearchHelpTemplate({
173
+ mode: this.currentMode,
174
+ modeHelp: SearchFieldWidget.getModeHelp(this.currentMode)
175
+ });
176
+ },
177
+ html: true,
178
+ sanitize: false
179
+ }).on('click', function () {
180
+ $(this).popover('toggle');
181
+ });
182
+
183
+ this.$('.g-search-mode-choose').popover({
184
+ trigger: 'manual',
185
+ viewport: {
186
+ selector: 'body',
187
+ padding: 10
188
+ },
189
+ content: () => {
190
+ return SearchModeSelectTemplate({
191
+ modes: this.modes,
192
+ currentMode: this.currentMode,
193
+ getModeDescription: SearchFieldWidget.getModeDescription
194
+ });
195
+ },
196
+ html: true,
197
+ sanitize: false
198
+ }).on('click', function () {
199
+ $(this).popover('toggle');
200
+ });
201
+
202
+ return this;
203
+ },
204
+
205
+ /**
206
+ * Parent views should call this if they wish to hide the result list.
207
+ */
208
+ hideResults: function () {
209
+ this.$('.dropdown').removeClass('open');
210
+ return this;
211
+ },
212
+
213
+ /**
214
+ * Parent views should call this if they wish to clear the search text.
215
+ */
216
+ clearText: function () {
217
+ this.$('.g-search-field').val('');
218
+ return this;
219
+ },
220
+
221
+ /**
222
+ * Parent views should call this if they wish to reset the search widget,
223
+ * i.e. clear it and hide any results.
224
+ */
225
+ resetState: function () {
226
+ return this.hideResults().clearText();
227
+ },
228
+
229
+ _animatePending: function () {
230
+ const isPending = this.ajaxLock;
231
+ this.$('.g-search-state')
232
+ .toggleClass('icon-search', !isPending)
233
+ .toggleClass('icon-spin4 animate-spin', isPending);
234
+ },
235
+
236
+ _doSearch: function (q) {
237
+ this.ajaxLock = true;
238
+ this.pending = null;
239
+ this._animatePending();
240
+
241
+ restRequest({
242
+ url: 'resource/search',
243
+ data: {
244
+ q: q,
245
+ mode: this.currentMode,
246
+ types: JSON.stringify(_.intersection(
247
+ this.types,
248
+ SearchFieldWidget.getModeTypes(this.currentMode))
249
+ )
250
+ }
251
+ }).done((results) => {
252
+ this.ajaxLock = false;
253
+ this._animatePending();
254
+
255
+ if (this.pending) {
256
+ this._doSearch(this.pending);
257
+ } else {
258
+ if (!this.$('.g-search-field').val()) {
259
+ // The search field is empty, so this widget probably had "this.resetState"
260
+ // called while the search was pending. So, don't render the (now obsolete)
261
+ // results.
262
+ return;
263
+ }
264
+
265
+ var resources = [];
266
+ _.each(this.types, function (type) {
267
+ _.each(results[type] || [], function (result) {
268
+ var text, icon;
269
+ if (type === 'user') {
270
+ text = result.firstName + ' ' + result.lastName +
271
+ ' (' + result.login + ')';
272
+ icon = 'user';
273
+ } else if (type === 'group') {
274
+ text = result.name;
275
+ icon = 'users';
276
+ } else if (type === 'collection') {
277
+ text = result.name;
278
+ icon = 'sitemap';
279
+ } else if (type === 'folder') {
280
+ text = result.name;
281
+ icon = 'folder';
282
+ } else if (type === 'item') {
283
+ text = result.name;
284
+ icon = 'doc-text-inv';
285
+ } else {
286
+ if (this.getInfoCallback) {
287
+ var res = this.getInfoCallback(type, result);
288
+ if (res) {
289
+ text = res.text;
290
+ icon = res.icon;
291
+ }
292
+ }
293
+ if (!text || !icon) {
294
+ text = '[unknown type]';
295
+ icon = 'attention';
296
+ }
297
+ }
298
+ resources.push({
299
+ type: type,
300
+ id: result._id,
301
+ text: text,
302
+ icon: icon
303
+ });
304
+ }, this);
305
+ }, this);
306
+ this.$('.g-search-results>ul').html(SearchResultsTemplate({
307
+ results: resources.slice(0, 6)
308
+ }));
309
+ this.$('.dropdown').addClass('open');
310
+ }
311
+ });
312
+ }
313
+ }, {
314
+ _allowedSearchMode: {},
315
+
316
+ addMode: function (mode, types, description, help) {
317
+ if (_.has(SearchFieldWidget._allowedSearchMode, mode)) {
318
+ throw new Error(`The mode "${mode}" exist already. You can't change it`);
319
+ }
320
+ SearchFieldWidget._allowedSearchMode[mode] = {
321
+ types: types,
322
+ description: description,
323
+ help: help
324
+ };
325
+ },
326
+
327
+ getModes: function () {
328
+ return _.keys(SearchFieldWidget._allowedSearchMode);
329
+ },
330
+
331
+ getModeTypes: function (mode) {
332
+ return SearchFieldWidget._allowedSearchMode[mode].types;
333
+ },
334
+
335
+ getModeDescription: function (mode) {
336
+ return SearchFieldWidget._allowedSearchMode[mode].description;
337
+ },
338
+
339
+ getModeHelp: function (mode) {
340
+ return SearchFieldWidget._allowedSearchMode[mode].help;
341
+ },
342
+
343
+ removeMode: function (mode) {
344
+ delete SearchFieldWidget._allowedSearchMode[mode];
345
+ }
346
+ });
347
+
348
+ SearchFieldWidget.addMode(
349
+ 'text',
350
+ ['item', 'folder', 'group', 'collection', 'user'],
351
+ 'Full text search',
352
+ `By default, search results will be returned if they contain
353
+ any of the terms of the search. If you wish to search for documents
354
+ containing all of the terms, place them in quotes.
355
+ Examples:`
356
+ );
357
+ SearchFieldWidget.addMode(
358
+ 'prefix',
359
+ ['item', 'folder', 'group', 'collection', 'user'],
360
+ 'Search by prefix',
361
+ `You are searching by prefix.
362
+ Start typing the first letters of whatever you are searching for.`
363
+ );
364
+
365
+ export default SearchFieldWidget;
@@ -0,0 +1,149 @@
1
+ import _ from 'underscore';
2
+
3
+ import View from '@girder/core/views/View';
4
+ import { restRequest } from '@girder/core/rest';
5
+
6
+ import PaginateWidgetTemplate from '@girder/core/templates/widgets/paginateWidget.pug';
7
+ import SearchFieldWidget from '@girder/core/views/widgets/SearchFieldWidget';
8
+ /**
9
+ * This widget is used to provide a consistent widget for iterating amongst
10
+ * pages of a list of search results (using a search mode, a query, an unique type,
11
+ * and a limit).
12
+ */
13
+
14
+ var SearchPaginateWidget = View.extend({
15
+ events: {
16
+ 'click .g-page-next:not(.disabled)': function () {
17
+ this._updateHasNextPage(true);
18
+ },
19
+ 'click .g-page-prev:not(.disabled)': function () {
20
+ this._updateHasPreviousPage(true);
21
+ }
22
+ },
23
+
24
+ initialize: function (settings) {
25
+ this._type = settings.type;
26
+ this._query = settings.query;
27
+ this._mode = settings.mode;
28
+ this._limit = settings.limit;
29
+
30
+ this._offset = 0;
31
+ this._currentPage = 0;
32
+ this._hasNextPage = false;
33
+ this._hasPreviousPage = false;
34
+
35
+ this.results = null;
36
+
37
+ this._updateHasNextPage();
38
+ },
39
+
40
+ render: function () {
41
+ this.$el.html(PaginateWidgetTemplate({
42
+ collection: this
43
+ }));
44
+
45
+ this.$('.g-page-next').girderEnable(this._hasNextPage);
46
+ this.$('.g-page-prev').girderEnable(this._hasPreviousPage);
47
+
48
+ if (!this._hasNextPage && !this._hasPreviousPage) {
49
+ this.$el.hide();
50
+ } else {
51
+ this.$el.show();
52
+ }
53
+
54
+ return this;
55
+ },
56
+
57
+ pageNum: function () {
58
+ return this._currentPage;
59
+ },
60
+
61
+ _updateHasPreviousPage: function (update = false) {
62
+ if (this._currentPage) {
63
+ if (update) {
64
+ return this._fetchPreviousPage(update)
65
+ .done(() => {
66
+ return this._fetchPreviousPage();
67
+ });
68
+ } else {
69
+ return this._fetchPreviousPage();
70
+ }
71
+ } else {
72
+ return this._fetchPreviousPage();
73
+ }
74
+ },
75
+
76
+ _updateHasNextPage: function (update = false) {
77
+ if (update) {
78
+ return this._fetchNextPage(update)
79
+ .done(() => {
80
+ return this._fetchNextPage();
81
+ });
82
+ } else {
83
+ return this._fetchNextPage();
84
+ }
85
+ },
86
+
87
+ _fetchPreviousPage: function (update = false) {
88
+ var offset = this._limit * (this._currentPage - 1);
89
+ if (offset < 0) {
90
+ this._hasPreviousPage = false;
91
+ this.render();
92
+ } else {
93
+ return this._fetch(offset)
94
+ .done((results) => {
95
+ var result = results[this._type];
96
+ if (result.length) {
97
+ this._hasPreviousPage = true;
98
+ if (update) {
99
+ this.results = result;
100
+ this.trigger('g:changed');
101
+ this._currentPage--;
102
+ this._updateHasNextPage();
103
+ }
104
+ } else {
105
+ this._hasPreviousPage = false;
106
+ }
107
+ this.render();
108
+ });
109
+ }
110
+ },
111
+
112
+ _fetchNextPage: function (update = false) {
113
+ var offset = this._limit * (this._currentPage + 1);
114
+ return this._fetch(offset)
115
+ .done((results) => {
116
+ var result = results[this._type];
117
+ if (result.length) {
118
+ this._hasNextPage = true;
119
+ if (update) {
120
+ this.results = result;
121
+ this.trigger('g:changed');
122
+ this._currentPage++;
123
+ this._updateHasPreviousPage();
124
+ }
125
+ } else {
126
+ this._hasNextPage = false;
127
+ }
128
+ this.render();
129
+ });
130
+ },
131
+
132
+ _fetch: function (offset) {
133
+ return restRequest({
134
+ url: 'resource/search',
135
+ data: {
136
+ q: this._query,
137
+ mode: this._mode,
138
+ types: JSON.stringify(_.intersection(
139
+ [this._type],
140
+ SearchFieldWidget.getModeTypes(this._mode))
141
+ ),
142
+ limit: this._limit,
143
+ offset: offset
144
+ }
145
+ });
146
+ }
147
+ });
148
+
149
+ export default SearchPaginateWidget;