1. 1 : /**
  2. 2 : * Document Terms is a table view of term frequencies for each document.
  3. 3 : *
  4. 4 : * @example
  5. 5 : *
  6. 6 : * let config = {
  7. 7 : * "bins": null,
  8. 8 : * "columns": null,
  9. 9 : * "dir": null,
  10. 10 : * "docId": null,
  11. 11 : * "docIndex": null,
  12. 12 : * "query": null,
  13. 13 : * "sort": null,
  14. 14 : * "stopList": null,
  15. 15 : * "termColors": null,
  16. 16 : * };
  17. 17 : *
  18. 18 : * loadCorpus("austen").tool("documentterms", config);
  19. 19 : *
  20. 20 : * @class DocumentTerms
  21. 21 : * @tutorial documentterms
  22. 22 : * @memberof Tools
  23. 23 : */
  24. 24 : Ext.define('Voyant.panel.DocumentTerms', {
  25. 25 : extend: 'Ext.grid.Panel',
  26. 26 : mixins: ['Voyant.panel.Panel'],
  27. 27 : requires: ['Voyant.data.store.DocumentTerms'],
  28. 28 : alias: 'widget.documentterms',
  29. 29 : config: {
  30. 30 : options: [{
  31. 31 : xtype: 'stoplistoption'
  32. 32 : },{
  33. 33 : xtype: 'categoriesoption'
  34. 34 : },{
  35. 35 : xtype: 'termcolorsoption'
  36. 36 : }]
  37. 37 : },
  38. 38 : statics: {
  39. 39 : i18n: {
  40. 40 : },
  41. 41 : api: {
  42. 42 : /**
  43. 43 : * @memberof Tools.DocumentTerms
  44. 44 : * @instance
  45. 45 : * @property {stopList}
  46. 46 : * @default
  47. 47 : */
  48. 48 : stopList: 'auto',
  49. 49 : /**
  50. 50 : * @memberof Tools.DocumentTerms
  51. 51 : * @instance
  52. 52 : * @property {query}
  53. 53 : */
  54. 54 : query: undefined,
  55. 55 : /**
  56. 56 : * @memberof Tools.DocumentTerms
  57. 57 : * @instance
  58. 58 : * @property {docId}
  59. 59 : */
  60. 60 : docId: undefined,
  61. 61 : /**
  62. 62 : * @memberof Tools.DocumentTerms
  63. 63 : * @instance
  64. 64 : * @property {docIndex}
  65. 65 : */
  66. 66 : docIndex: undefined,
  67. 67 : /**
  68. 68 : * @memberof Tools.DocumentTerms
  69. 69 : * @instance
  70. 70 : * @property {bins}
  71. 71 : * @default
  72. 72 : */
  73. 73 : bins: 10,
  74. 74 : /**
  75. 75 : * @memberof Tools.DocumentTerms
  76. 76 : * @instance
  77. 77 : * @property {columns} columns 'docIndex', 'term', 'rawFreq', 'relativeFreq', 'tfidf', 'zscore', 'distributions'
  78. 78 : */
  79. 79 : columns: undefined,
  80. 80 : /**
  81. 81 : * @memberof Tools.DocumentTerms
  82. 82 : * @instance
  83. 83 : * @property {sort}
  84. 84 : */
  85. 85 : sort: undefined,
  86. 86 : /**
  87. 87 : * @memberof Tools.DocumentTerms
  88. 88 : * @instance
  89. 89 : * @property {dir}
  90. 90 : */
  91. 91 : dir: undefined,
  92. 92 : /**
  93. 93 : * @memberof Tools.DocumentTerms
  94. 94 : * @instance
  95. 95 : * @property {termColors}
  96. 96 : * @default
  97. 97 : */
  98. 98 : termColors: 'categories'
  99. 99 : },
  100. 100 : glyph: 'xf0ce@FontAwesome'
  101. 101 : },
  102. 102 : constructor: function(config) {
  103. 103 :
  104. 104 : this.callParent(arguments);
  105. 105 : this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
  106. 106 :
  107. 107 : // create a listener for corpus loading (defined here, in case we need to load it next)
  108. 108 : this.on('loadedCorpus', function(src, corpus) {
  109. 109 : var store = this.getStore();
  110. 110 : store.setCorpus(corpus);
  111. 111 : if (this.isVisible()) {
  112. 112 : store.load();
  113. 113 : }
  114. 114 : });
  115. 115 :
  116. 116 : if (config.embedded) {
  117. 117 : if (window.console) {
  118. 118 : console.warn(config.embedded.then);
  119. 119 : }
  120. 120 : var cls = Ext.getClass(config.embedded).getName();
  121. 121 : if (cls=="Voyant.data.store.DocumentTerms" || cls=="Voyant.data.model.Document") {
  122. 122 : this.fireEvent('loadedCorpus', this, config.embedded.getCorpus());
  123. 123 : }
  124. 124 : }
  125. 125 : else if (config.corpus) {
  126. 126 : this.fireEvent('loadedCorpus', this, config.corpus);
  127. 127 : }
  128. 128 :
  129. 129 : this.on("query", function(src, query) {
  130. 130 : this.fireEvent("corpusTermsClicked", src, query);
  131. 131 : }, this);
  132. 132 :
  133. 133 : this.on("corpusTermsClicked", function(src, terms) {
  134. 134 : if (this.getStore().getCorpus()) { // make sure we have a corpus
  135. 135 : var query = [];
  136. 136 : terms.forEach(function(term) {
  137. 137 : query.push(Ext.isString(term) ? term : term.get("term"));
  138. 138 : });
  139. 139 : this.setApiParams({
  140. 140 : query: query,
  141. 141 : docId: undefined,
  142. 142 : docIndex: undefined
  143. 143 : });
  144. 144 : if (this.isVisible()) {
  145. 145 : this.getStore().load({params: this.getApiParams()});
  146. 146 : }
  147. 147 : }
  148. 148 : });
  149. 149 :
  150. 150 : this.on("documentsClicked", function(src, documents) {
  151. 151 : var docIds = [];
  152. 152 : documents.forEach(function(doc) {docIds.push(doc.get('id'));});
  153. 153 : this.setApiParams({
  154. 154 : docId: docIds,
  155. 155 : query: undefined
  156. 156 : });
  157. 157 : if (this.isVisible()) {
  158. 158 : this.getStore().load({params: this.getApiParams()});
  159. 159 : }
  160. 160 : });
  161. 161 :
  162. 162 : this.on("documentsSelected", function(src, docIds) {
  163. 163 : this.setApiParams({
  164. 164 : docId: docIds,
  165. 165 : query: undefined
  166. 166 : });
  167. 167 : if (this.isVisible()) {
  168. 168 : this.getStore().load({params: this.getApiParams()});
  169. 169 : }
  170. 170 : });
  171. 171 :
  172. 172 : this.on("corpusSelected", function(src, corpus) {
  173. 173 : this.setApiParams({
  174. 174 : docId: undefined,
  175. 175 : docIndex: undefined
  176. 176 : });
  177. 177 : if (this.isVisible()) {
  178. 178 : this.getStore().load({params: this.getApiParams()});
  179. 179 : }
  180. 180 : });
  181. 181 :
  182. 182 : this.on("activate", function() { // load after tab activate (if we're in a tab panel)
  183. 183 : if (this.getStore().getCorpus()) {
  184. 184 : this.getStore().load({params: this.getApiParams()});
  185. 185 : }
  186. 186 : }, this);
  187. 187 : },
  188. 188 :
  189. 189 : initComponent: function() {
  190. 190 : var me = this;
  191. 191 :
  192. 192 : var store = Ext.create("Voyant.data.store.DocumentTermsBuffered", {parentPanel: this});
  193. 193 :
  194. 194 : Ext.apply(me, {
  195. 195 : title: this.localize('title'),
  196. 196 : emptyText: this.localize("emptyText"),
  197. 197 : store : store,
  198. 198 : selModel: Ext.create('Ext.selection.CheckboxModel', {
  199. 199 : listeners: {
  200. 200 : selectionchange: {
  201. 201 : fn: function(sm, selections) {
  202. 202 : this.getApplication().dispatchEvent('documentTermsClicked', this, selections);
  203. 203 : },
  204. 204 : scope: this
  205. 205 : }
  206. 206 : },
  207. 207 : pruneRemoved: false,
  208. 208 : mode: 'SIMPLE'
  209. 209 : }),
  210. 210 : dockedItems: [{
  211. 211 : dock: 'bottom',
  212. 212 : xtype: 'toolbar',
  213. 213 : overflowHandler: 'scroller',
  214. 214 : items: [{
  215. 215 : xtype: 'querysearchfield'
  216. 216 : }, {
  217. 217 : xtype: 'totalpropertystatus'
  218. 218 : },{
  219. 219 : xtype: 'corpusdocumentselector'
  220. 220 : }]
  221. 221 : }],
  222. 222 : columns: [{
  223. 223 : text: '#',
  224. 224 : width: 30,
  225. 225 : dataIndex: 'docIndex',
  226. 226 : sortable: true,
  227. 227 : renderer: function(v) {return v+1;} // 0-based to 1-based
  228. 228 : },{
  229. 229 : text: this.localize("term"),
  230. 230 : dataIndex: 'term',
  231. 231 : tooltip: this.localize("termTip"),
  232. 232 : sortable: true,
  233. 233 : flex: 1,
  234. 234 : xtype: 'coloredtermfield',
  235. 235 : useCategoriesMenu: true
  236. 236 : },{
  237. 237 : text: this.localize("rawFreq"),
  238. 238 : dataIndex: 'rawFreq',
  239. 239 : tooltip: this.localize("rawFreqTip"),
  240. 240 : width: 'autoSize',
  241. 241 : sortable: true
  242. 242 : },{
  243. 243 : text: this.localize("relativeFreq"),
  244. 244 : tooltip: this.localize("relativeFreqTip"),
  245. 245 : dataIndex: 'relativeFreq',
  246. 246 : width: 'autoSize',
  247. 247 : sortable: true,
  248. 248 : renderer: function(val) {
  249. 249 : return Ext.util.Format.number(val*1000000, "0,000")
  250. 250 : }
  251. 251 : },{
  252. 252 : text: this.localize("tfidf"),
  253. 253 : tooltip: this.localize("tfidfTip"),
  254. 254 : dataIndex: 'tfidf',
  255. 255 : width: 'autoSize',
  256. 256 : sortable: true,
  257. 257 : hidden: true,
  258. 258 : renderer: Ext.util.Format.numberRenderer('0,000.000')
  259. 259 : },{
  260. 260 : text: this.localize("zscore"),
  261. 261 : tooltip: this.localize("zscoreTip"),
  262. 262 : dataIndex: 'zscore',
  263. 263 : width: 'autoSize',
  264. 264 : sortable: true,
  265. 265 : hidden: true,
  266. 266 : renderer: Ext.util.Format.numberRenderer('0,000.000')
  267. 267 : },{
  268. 268 : xtype: 'widgetcolumn',
  269. 269 : text: this.localize("trend"),
  270. 270 : tooltip: this.localize('trendTip'),
  271. 271 : flex: 1,
  272. 272 : dataIndex: 'distributions',
  273. 273 : widget: {
  274. 274 : xtype: 'sparklineline'
  275. 275 : }
  276. 276 : }],
  277. 277 :
  278. 278 : listeners: {
  279. 279 : termsClicked: {
  280. 280 : fn: function(src, terms) {
  281. 281 : if (this.getStore().getCorpus()) { // make sure we have a corpus
  282. 282 : var queryTerms = [];
  283. 283 : terms.forEach(function(term) {
  284. 284 : if (Ext.isString(term)) {queryTerms.push(term);}
  285. 285 : else if (term.term) {queryTerms.push(term.term);}
  286. 286 : else if (term.getTerm) {queryTerms.push(term.getTerm());}
  287. 287 : });
  288. 288 : if (queryTerms.length > 0) {
  289. 289 : this.setApiParams({
  290. 290 : docIndex: undefined,
  291. 291 : docId: undefined,
  292. 292 : query: queryTerms
  293. 293 : });
  294. 294 : if (this.isVisible()) {
  295. 295 : if (this.isVisible()) {
  296. 296 : this.getStore().load({params: this.getApiParams()});
  297. 297 : }
  298. 298 : }
  299. 299 : }
  300. 300 : }
  301. 301 : },
  302. 302 : scope: this
  303. 303 : }
  304. 304 : }
  305. 305 : });
  306. 306 :
  307. 307 : me.callParent(arguments);
  308. 308 :
  309. 309 : me.getStore().getProxy().setExtraParam("withDistributions", true);
  310. 310 :
  311. 311 : }
  312. 312 :
  313. 313 : });