1. 1 : /**
  2. 2 : * Each member of Tools is available for use with the {@link Spyral.Corpus#tool} method.
  3. 3 : * @namespace Tools
  4. 4 : */
  5. 5 :
  6. 6 : /**
  7. 7 : * The base class for Voyant tool panels.
  8. 8 : *
  9. 9 : * @class Panel
  10. 10 : * @memberof Tools
  11. 11 : */
  12. 12 : Ext.define('Voyant.panel.Panel', {
  13. 13 : mixins: ['Voyant.util.Localization','Voyant.util.Api','Voyant.util.Toolable','Voyant.util.DetailedError'],
  14. 14 : requires: ['Voyant.widget.QuerySearchField','Voyant.widget.StopListOption','Voyant.categories.CategoriesOption','Voyant.widget.TotalPropertyStatus'],
  15. 15 : alias: 'widget.voyantpanel',
  16. 16 : statics: {
  17. 17 : i18n: {
  18. 18 : },
  19. 19 : config: {
  20. 20 : corpusValidated: false
  21. 21 : },
  22. 22 : // typedefs for commonly used API params
  23. 23 : /**
  24. 24 : * @typedef {String} stopList A comma-separated list of words, a named list or a URL to a plain text list, one word per line.
  25. 25 : * By default this is set to 'auto' which auto-detects the document's language and loads an appropriate list (if available for that language). Set this to blank to not use the default stopList.
  26. 26 : * For more information see the [Stopwords documentation]{@tutorial stopwords}.
  27. 27 : */
  28. 28 :
  29. 29 : /**
  30. 30 : * @typedef {String|String[]} query A query or array of queries (queries can be separated by a comma).
  31. 31 : * For query syntax, see the [search documentation]{@tutorial search}.
  32. 32 : */
  33. 33 :
  34. 34 : /**
  35. 35 : * @typedef {String|String[]} docId The document ID(s) to restrict the results to.
  36. 36 : */
  37. 37 :
  38. 38 : /**
  39. 39 : * @typedef {Number|Number[]} docIndex The document index(es) to restrict the results to.
  40. 40 : */
  41. 41 :
  42. 42 : /**
  43. 43 : * @typedef {String} categories The categories ID to use. For more information see the [Categories documentation]{@tutorial categories}.
  44. 44 : */
  45. 45 :
  46. 46 : /**
  47. 47 : * @typedef {Number} bins The number of "bins" to divide the result into.
  48. 48 : */
  49. 49 :
  50. 50 : /**
  51. 51 : * @typedef {Number} start The index of the item to start the results at.
  52. 52 : */
  53. 53 :
  54. 54 : /**
  55. 55 : * @typedef {Number} limit The number of items to limit the result to.
  56. 56 : */
  57. 57 :
  58. 58 : /**
  59. 59 : * @typedef {Number} context The number of terms to consider on each side of the keyword.
  60. 60 : */
  61. 61 :
  62. 62 : /**
  63. 63 : * @typedef {String} withDistributions Determines whether to show "raw" or "relative" frequencies (those are the two valid values).
  64. 64 : * The default value is "relative" (unless there's only one document in the corpus, in which case raw frequencies are shown).
  65. 65 : */
  66. 66 :
  67. 67 : /**
  68. 68 : * @typedef {String} termColors Which term colors to show in the grid.
  69. 69 : * By default this is set to 'categories' which shows the term color only if it's been assigned by a category.
  70. 70 : * The other alternatives are 'terms' which shows all terms colors, and '' or undefined which shows no term colors.
  71. 71 : */
  72. 72 :
  73. 73 : /**
  74. 74 : * @typedef {String|String[]} columns One or more column data indexes to display, separated by a comma.
  75. 75 : * Use this to modify the default set of visible columns.
  76. 76 : */
  77. 77 :
  78. 78 : /**
  79. 79 : * @typedef {String} sort The column to sort the results by
  80. 80 : */
  81. 81 :
  82. 82 : /**
  83. 83 : * @typedef {String} dir The direction in which to sort the results: 'asc' or 'desc'
  84. 84 : */
  85. 85 : api: {
  86. 86 : /**
  87. 87 : * @memberof Tools.Panel
  88. 88 : * @instance
  89. 89 : * @property {String} corpus The ID of the corpus to use.
  90. 90 : */
  91. 91 : corpus: undefined,
  92. 92 :
  93. 93 : /**
  94. 94 : * @memberof Tools.Panel
  95. 95 : * @instance
  96. 96 : * @property {String|String[]} input Use to directly provide input, instead of specifying a corpus. Can be: one or more URLs, one or more chunks of text.
  97. 97 : */
  98. 98 : input: undefined,
  99. 99 :
  100. 100 : /**
  101. 101 : * @memberof Tools.Panel
  102. 102 : * @instance
  103. 103 : * @property {String} inputFormat The input format of the provided input (the default is auto-detect).
  104. 104 : */
  105. 105 : inputFormat: undefined,
  106. 106 :
  107. 107 : /**
  108. 108 : * @memberof Tools.Panel
  109. 109 : * @instance
  110. 110 : * @property {String} subtitle Specify a subtitle to display in the tool's header.
  111. 111 : */
  112. 112 : subtitle: undefined
  113. 113 : }
  114. 114 : },
  115. 115 : config: {
  116. 116 : corpus: undefined
  117. 117 : },
  118. 118 : constructor: function(config) {
  119. 119 : this.mixins['Voyant.util.Api'].constructor.apply(this, arguments);
  120. 120 : this.mixins['Voyant.util.Toolable'].constructor.apply(this, arguments);
  121. 121 : if (!this.glyph) {
  122. 122 : this.glyph = Ext.ClassManager.getClass(this).glyph
  123. 123 : }
  124. 124 :
  125. 125 : this.on("afterrender", function() {
  126. 126 : if (this.getXType()!='facet' && this.getApiParam('subtitle') && this.getTitle()) {
  127. 127 : this.setTitle(this.getTitle()+" <i style='font-size: smaller;'>"+this.getApiParam('subtitle')+"</i>")
  128. 128 : }
  129. 129 : if (this.isXType("grid")) {
  130. 130 : this.on('boxready', function() {
  131. 131 : var columns = this.getApiParam('columns');
  132. 132 : if (columns !== undefined) {
  133. 133 : if (Array.isArray(columns) === false) {
  134. 134 : columns = columns.split(',');
  135. 135 : }
  136. 136 : if (columns.length > 0) {
  137. 137 : var anyMatch = false;
  138. 138 : this.getColumns().forEach(function(gcol) {
  139. 139 : var match = columns.indexOf(gcol.dataIndex) !== -1;
  140. 140 : anyMatch = match || anyMatch;
  141. 141 : gcol.setVisible(match);
  142. 142 : });
  143. 143 : if (!anyMatch) {
  144. 144 : this.getColumns().forEach(function(gcol) {
  145. 145 : gcol.setVisible(gcol.initialConfig.hidden !== undefined ? !gcol.initialConfig.hidden : true);
  146. 146 : });
  147. 147 : }
  148. 148 : }
  149. 149 : }
  150. 150 : }, this);
  151. 151 :
  152. 152 : this.getSelectionModel().on("selectionchange", function(store, records) {
  153. 153 : // console.warn(records, this.selectedRecordsToRemember)
  154. 154 : // this.selectedRecordsToRemember = records;
  155. 155 : }, this);
  156. 156 : this.getStore().on("beforeload", function() {
  157. 157 : this.selectedRecordsToRemember = this.getSelection();
  158. 158 : }, this)
  159. 159 : this.getStore().on("load", function(store, records) {
  160. 160 : if (Ext.Array.from(this.selectedRecordsToRemember).length>0) {
  161. 161 : // combine contents of store with contents of remembered items, filtering out duplicates
  162. 162 : var seen = {}
  163. 163 : var mergedRecords = Ext.Array.merge(this.selectedRecordsToRemember, records).filter(function(item) {
  164. 164 : if (!(item.getId() in seen)) {
  165. 165 : seen[item.getId()]=true;
  166. 166 : return true
  167. 167 : } else {
  168. 168 : return false;
  169. 169 : }
  170. 170 : });
  171. 171 : if (store.isBufferedStore) {
  172. 172 : if (store.currentPage==1) {
  173. 173 : store.data.addAll(mergedRecords);
  174. 174 : store.totalCount = mergedRecords.length;
  175. 175 : store.fireEvent('refresh', store);
  176. 176 : }
  177. 177 : } else {
  178. 178 : store.loadRecords(mergedRecords);
  179. 179 : this.getSelectionModel().select(this.selectedRecordsToRemember);
  180. 180 : store.fireEvent('refresh', store);
  181. 181 : this.selectedRecordsToRemember = [];
  182. 182 : }
  183. 183 : }
  184. 184 : }, this);
  185. 185 : }
  186. 186 : }, this);
  187. 187 :
  188. 188 : this.on({
  189. 189 : loadedCorpus: {
  190. 190 : fn: function(src, corpus) {
  191. 191 : // make sure API is updated if we had a corpus and it's changed, this should be registered first, so hopefully be fired before tools receive notification
  192. 192 : this.setApiParam("corpus", corpus.getAliasOrId());
  193. 193 : this.setCorpus(corpus);
  194. 194 : },
  195. 195 : priority: 999, // very high priority
  196. 196 : scope: this
  197. 197 : }
  198. 198 : });
  199. 199 : },
  200. 200 :
  201. 201 : getApplication: function() {
  202. 202 : return Voyant.application;
  203. 203 : },
  204. 204 :
  205. 205 : getBaseUrl: function() {
  206. 206 : return this.getApplication().getBaseUrl();
  207. 207 : },
  208. 208 :
  209. 209 : openUrl: function(url) {
  210. 210 : this.getApplication().openUrl.apply(this, arguments);
  211. 211 : },
  212. 212 :
  213. 213 : getTromboneUrl: function() {
  214. 214 : return this.getApplication().getTromboneUrl();
  215. 215 : },
  216. 216 :
  217. 217 : dispatchEvent: function() {
  218. 218 : var application = this.getApplication();
  219. 219 : application.dispatchEvent.apply(application, arguments);
  220. 220 : },
  221. 221 :
  222. 222 : showError: function(config, response) {
  223. 223 : if (Ext.isString(config)) {
  224. 224 : config = {
  225. 225 : message: config
  226. 226 : }
  227. 227 : }
  228. 228 : Ext.applyIf(config, {
  229. 229 : title: this.localize("error")+" ("+this.localize("title")+")"
  230. 230 : })
  231. 231 : this.getApplication().showError(config, response)
  232. 232 : },
  233. 233 :
  234. 234 : showResponseError: function(config, response) {
  235. 235 : this.getApplication().showResponseError(config, response)
  236. 236 : },
  237. 237 :
  238. 238 : toastError: function(config) {
  239. 239 : if (Ext.isString(config)) {
  240. 240 : config = {html: config}
  241. 241 : }
  242. 242 : Ext.applyIf(config, {
  243. 243 : glyph: 'xf071@FontAwesome',
  244. 244 : title: this.localize("error")
  245. 245 : })
  246. 246 : this.toast(config);
  247. 247 : },
  248. 248 :
  249. 249 : toastInfo: function(config) {
  250. 250 : if (Ext.isString(config)) {
  251. 251 : config = {html: config}
  252. 252 : }
  253. 253 : Ext.applyIf(config, {
  254. 254 : glyph: 'xf05a@FontAwesome',
  255. 255 : title: this.localize("info")
  256. 256 : })
  257. 257 : this.toast(config);
  258. 258 : },
  259. 259 :
  260. 260 : toast: function(config) {
  261. 261 : if (Ext.isString(config)) {
  262. 262 : config = {html: config}
  263. 263 : }
  264. 264 : Ext.applyIf(config, {
  265. 265 : slideInDuration: 500,
  266. 266 : shadow: true,
  267. 267 : align: 'b',
  268. 268 : anchor: this.getTargetEl()
  269. 269 : })
  270. 270 : Ext.toast(config);
  271. 271 : },
  272. 272 :
  273. 273 : /**
  274. 274 : * Checks to see if we have access to this corpus, first by checking the application's
  275. 275 : * access setting for the corpus, then by checking the corpus setting.
  276. 276 : * @private
  277. 277 : */
  278. 278 : hasCorpusAccess: function(corpus) {
  279. 279 : var app = this.getApplication();
  280. 280 : var corpusAccess = app.getCorpusAccess(); // undefined: corpus is unprotected, ACCESS: user entered the access password, ADMIN: user entered there admin password, LIMITED: no password entered
  281. 281 : if (corpusAccess === 'ADMIN' || corpusAccess === 'ACCESS') return true;
  282. 282 : if (!corpus) {
  283. 283 : corpus = this.getCorpus();
  284. 284 : if (!corpus) {
  285. 285 : corpus = app.getCorpus();
  286. 286 : }
  287. 287 : }
  288. 288 : return corpus.getNoPasswordAccess() === 'NORMAL'; // NORMAL: corpus is unprotected, NONE: corpus cannot be accessed except by password, NONCONSUMPTIVE: limited access without password
  289. 289 : },
  290. 290 :
  291. 291 : /**
  292. 292 : * Checks to see if the user can modify the corpus.
  293. 293 : * @private
  294. 294 : */
  295. 295 : hasModifyCorpusAccess: function(corpus) {
  296. 296 : var allowDownload = this.getApplication().getAllowDownload();
  297. 297 : if (!allowDownload) return false;
  298. 298 :
  299. 299 : var allowInput = this.getApplication().getAllowInput();
  300. 300 : if (!allowInput) return false;
  301. 301 :
  302. 302 : var corpusAccess = this.getApplication().getCorpusAccess();
  303. 303 : var noPasswordAccess = corpus.getNoPasswordAccess();
  304. 304 :
  305. 305 : var canModify = (corpusAccess === undefined && noPasswordAccess === 'NORMAL') || corpusAccess === 'ADMIN';
  306. 306 :
  307. 307 : return canModify;
  308. 308 : }
  309. 309 : });