1. 1 : /**
  2. 2 : * Corpus Collocates is a table view of which terms appear more frequently in proximity to keywords across the entire corpus.
  3. 3 : *
  4. 4 : * @example
  5. 5 : *
  6. 6 : * let config = {
  7. 7 : * columns: null,
  8. 8 : * context: 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("CorpusCollocates", config);
  19. 19 : *
  20. 20 : *
  21. 21 : * @class CorpusCollocates
  22. 22 : * @tutorial corpuscollocates
  23. 23 : * @memberof Tools
  24. 24 : */
  25. 25 : Ext.define('Voyant.panel.CorpusCollocates', {
  26. 26 : extend: 'Ext.grid.Panel',
  27. 27 : mixins: ['Voyant.panel.Panel'],
  28. 28 : alias: 'widget.corpuscollocates',
  29. 29 : statics: {
  30. 30 : i18n: {
  31. 31 : },
  32. 32 : api: {
  33. 33 : /**
  34. 34 : * @memberof Tools.CorpusCollocates
  35. 35 : * @instance
  36. 36 : * @property {stopList}
  37. 37 : * @default
  38. 38 : */
  39. 39 : stopList: 'auto',
  40. 40 :
  41. 41 : /**
  42. 42 : * @memberof Tools.CorpusCollocates
  43. 43 : * @instance
  44. 44 : * @property {context}
  45. 45 : * @default
  46. 46 : */
  47. 47 : context: 5,
  48. 48 :
  49. 49 : /**
  50. 50 : * @memberof Tools.CorpusCollocates
  51. 51 : * @instance
  52. 52 : * @property {query}
  53. 53 : */
  54. 54 : query: undefined,
  55. 55 :
  56. 56 : /**
  57. 57 : * @memberof Tools.CorpusCollocates
  58. 58 : * @instance
  59. 59 : * @property {docId}
  60. 60 : */
  61. 61 : docId: undefined,
  62. 62 :
  63. 63 : /**
  64. 64 : * @memberof Tools.CorpusCollocates
  65. 65 : * @instance
  66. 66 : * @property {docIndex}
  67. 67 : */
  68. 68 : docIndex: undefined,
  69. 69 :
  70. 70 : /**
  71. 71 : * @memberof Tools.CorpusCollocates
  72. 72 : * @instance
  73. 73 : * @property {columns} columns 'term', 'rawFreq', 'contextTerm', 'contextTermRawFreq'
  74. 74 : */
  75. 75 : columns: undefined,
  76. 76 :
  77. 77 : /**
  78. 78 : * @memberof Tools.CorpusCollocates
  79. 79 : * @instance
  80. 80 : * @property {sort}
  81. 81 : */
  82. 82 : sort: undefined,
  83. 83 :
  84. 84 : /**
  85. 85 : * @memberof Tools.CorpusCollocates
  86. 86 : * @instance
  87. 87 : * @property {dir}
  88. 88 : */
  89. 89 : dir: undefined,
  90. 90 :
  91. 91 : /**
  92. 92 : * @memberof Tools.CorpusCollocates
  93. 93 : * @instance
  94. 94 : * @property {termColors}
  95. 95 : * @default
  96. 96 : */
  97. 97 : termColors: 'categories'
  98. 98 : },
  99. 99 : glyph: 'xf0ce@FontAwesome'
  100. 100 : },
  101. 101 : config: {
  102. 102 : options: [{xtype: 'stoplistoption'},{xtype: 'categoriesoption'},{xtype: 'termcolorsoption'}]
  103. 103 : },
  104. 104 : constructor: function(config) {
  105. 105 :
  106. 106 : this.callParent(arguments);
  107. 107 : this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
  108. 108 :
  109. 109 : // create a listener for corpus loading (defined here, in case we need to load it next)
  110. 110 : this.on('loadedCorpus', function(src, corpus) {
  111. 111 : if (this.isVisible()) {
  112. 112 : this.loadFromApis();
  113. 113 : }
  114. 114 : });
  115. 115 :
  116. 116 : if (config.embedded) {
  117. 117 : // var cls = Ext.getClass(config.embedded).getName();
  118. 118 : // if (cls=="Voyant.data.store.DocumentTerms" || cls=="Voyant.data.model.Document") {
  119. 119 : // this.fireEvent('loadedCorpus', this, config.embedded.getCorpus())
  120. 120 : // }
  121. 121 : }
  122. 122 : else if (config.corpus) {
  123. 123 : this.fireEvent('loadedCorpus', this, config.corpus)
  124. 124 : }
  125. 125 :
  126. 126 : this.on("corpusTermsClicked", function(src, terms) {
  127. 127 : if (this.getStore().getCorpus()) { // make sure we have a corpus
  128. 128 : var query = [];
  129. 129 : terms.forEach(function(term) {
  130. 130 : query.push(term.get("term"));
  131. 131 : })
  132. 132 : this.setApiParams({
  133. 133 : query: query,
  134. 134 : docId: undefined,
  135. 135 : docIndex: undefined
  136. 136 : });
  137. 137 : if (this.isVisible()) {
  138. 138 : this.loadFromApis();
  139. 139 : }
  140. 140 : }
  141. 141 : });
  142. 142 :
  143. 143 : this.on("documentsClicked", function(src, documents) {
  144. 144 : var docIds = [];
  145. 145 : documents.forEach(function(doc) {docIds.push(doc.get('id'))});
  146. 146 : this.setApiParams({
  147. 147 : docId: docIds,
  148. 148 : docid: undefined,
  149. 149 : query: undefined
  150. 150 : })
  151. 151 : if (this.isVisible()) {
  152. 152 : this.loadFromApis();
  153. 153 : }
  154. 154 : });
  155. 155 :
  156. 156 : this.on("activate", function() { // load after tab activate (if we're in a tab panel)
  157. 157 : if (this.getStore().getCorpus()) {this.loadFromApis()}
  158. 158 : }, this)
  159. 159 :
  160. 160 : this.on("query", function(src, query) {
  161. 161 : if (query.length === 0) query = undefined;
  162. 162 : this.setApiParam("query", query);
  163. 163 : this.getStore().getProxy().setExtraParam("query", query);
  164. 164 : this.loadFromApis();
  165. 165 : }, this)
  166. 166 : },
  167. 167 :
  168. 168 : loadFromApis: function() {
  169. 169 : if (this.getStore().getCorpus()) {
  170. 170 : if (this.getApiParam('query')) {
  171. 171 : this.getStore().clearAndLoad({params: this.getApiParams()});
  172. 172 : }
  173. 173 : else {
  174. 174 : var corpusTerms = this.getStore().getCorpus().getCorpusTerms({
  175. 175 : leadingBufferZone: 0,
  176. 176 : autoLoad: false
  177. 177 : });
  178. 178 : corpusTerms.load({
  179. 179 : callback: function(records, operation, success) {
  180. 180 : if (success) {
  181. 181 : var terms = [];
  182. 182 : records.forEach(function(term) {
  183. 183 : terms.push(term.getTerm());
  184. 184 : })
  185. 185 : this.getStore().getProxy().setExtraParam("query", terms);
  186. 186 : this.setApiParam('query', terms);
  187. 187 : this.loadFromApis();
  188. 188 : }
  189. 189 : },
  190. 190 : scope: this,
  191. 191 : params: {
  192. 192 : limit: 10,
  193. 193 : stopList: this.getApiParam("stopList")
  194. 194 : }
  195. 195 : });
  196. 196 :
  197. 197 : }
  198. 198 : }
  199. 199 : },
  200. 200 :
  201. 201 : initComponent: function() {
  202. 202 : var me = this;
  203. 203 :
  204. 204 : var store = Ext.create("Voyant.data.store.CorpusCollocatesBuffered", {parentPanel: this});
  205. 205 :
  206. 206 : Ext.apply(me, {
  207. 207 : title: this.localize('title'),
  208. 208 : emptyText: this.localize("emptyText"),
  209. 209 : store : store,
  210. 210 : selModel: Ext.create('Ext.selection.CheckboxModel', {
  211. 211 : listeners: {
  212. 212 : selectionchange: {
  213. 213 : fn: function(sm, selections) {
  214. 214 : if (selections.length > 0) {
  215. 215 : var terms = [];
  216. 216 : var context = this.getApiParam("context")
  217. 217 : selections.forEach(function(selection) {
  218. 218 : terms.push('"'+selection.getKeyword()+" "+selection.getContextTerm()+'"~'+context)
  219. 219 : })
  220. 220 : this.getApplication().dispatchEvent('termsClicked', this, terms);
  221. 221 : }
  222. 222 : },
  223. 223 : scope: this
  224. 224 : }
  225. 225 : }
  226. 226 : }),
  227. 227 : dockedItems: [{
  228. 228 : dock: 'bottom',
  229. 229 : xtype: 'toolbar',
  230. 230 : overflowHandler: 'scroller',
  231. 231 : items: [{
  232. 232 : xtype: 'querysearchfield'
  233. 233 : }, {
  234. 234 : xtype: 'totalpropertystatus'
  235. 235 : }, this.localize('context'), {
  236. 236 : xtype: 'slider',
  237. 237 : minValue: 1,
  238. 238 : value: 5,
  239. 239 : maxValue: 30,
  240. 240 : increment: 2,
  241. 241 : width: 50,
  242. 242 : listeners: {
  243. 243 : render: function(slider) {
  244. 244 : slider.setValue(me.getApiParam('context'))
  245. 245 : },
  246. 246 : changecomplete: function(slider, newValue) {
  247. 247 : me.setApiParam("context", slider.getValue());
  248. 248 : me.loadFromApis();
  249. 249 : }
  250. 250 : }
  251. 251 : },{
  252. 252 : xtype: 'corpusdocumentselector'
  253. 253 : }]
  254. 254 : }],
  255. 255 : columns: [{
  256. 256 : text: this.localize("term"),
  257. 257 : dataIndex: 'term',
  258. 258 : tooltip: this.localize("termTip"),
  259. 259 : sortable: true,
  260. 260 : flex: 1,
  261. 261 : xtype: 'coloredtermfield',
  262. 262 : useCategoriesMenu: true
  263. 263 : },{
  264. 264 : text: this.localize("rawFreq"),
  265. 265 : dataIndex: 'rawFreq',
  266. 266 : tooltip: this.localize("termRawFreqTip"),
  267. 267 : sortable: true,
  268. 268 : width: 'autoSize',
  269. 269 : hidden: true
  270. 270 : },{
  271. 271 : text: this.localize("contextTerm"),
  272. 272 : dataIndex: 'contextTerm',
  273. 273 : tooltip: this.localize("contextTermTip"),
  274. 274 : flex: 1,
  275. 275 : sortable: true,
  276. 276 : xtype: 'coloredtermfield'
  277. 277 : },{
  278. 278 : text: this.localize("contextTermRawFreq"),
  279. 279 : tooltip: this.localize("contextTermRawFreqTip"),
  280. 280 : dataIndex: 'contextTermRawFreq',
  281. 281 : width: 'autoSize',
  282. 282 : sortable: true
  283. 283 : }/*,{
  284. 284 : xtype: 'widgetcolumn',
  285. 285 : text: this.localize("trend"),
  286. 286 : tooltip: this.localize('trendTip'),
  287. 287 : width: 120,
  288. 288 : dataIndex: 'distributions',
  289. 289 : widget: {
  290. 290 : xtype: 'sparklineline'
  291. 291 : }
  292. 292 : }*/],
  293. 293 :
  294. 294 : listeners: {
  295. 295 : scope: this,
  296. 296 : termsClicked: function(src, terms) {
  297. 297 : if (this.getStore().getCorpus()) { // make sure we have a corpus
  298. 298 : var queryTerms = [];
  299. 299 : terms.forEach(function(term) {
  300. 300 : if (Ext.isString(term)) {queryTerms.push(term);}
  301. 301 : else if (term.term) {queryTerms.push(term.term);}
  302. 302 : else if (term.getTerm) {queryTerms.push(term.getTerm());}
  303. 303 : });
  304. 304 : if (queryTerms.length > 0) {
  305. 305 : this.setApiParams({
  306. 306 : docIndex: undefined,
  307. 307 : docId: undefined,
  308. 308 : query: queryTerms
  309. 309 : });
  310. 310 : if (this.isVisible()) {
  311. 311 : this.loadFromApis();
  312. 312 : }
  313. 313 : }
  314. 314 : }
  315. 315 : },
  316. 316 :
  317. 317 : corpusSelected: function() {
  318. 318 : if (this.getStore().getCorpus()) {
  319. 319 : this.setApiParams({docId: undefined, docIndex: undefined})
  320. 320 : this.loadFromApis();
  321. 321 : }
  322. 322 : },
  323. 323 :
  324. 324 : documentsSelected: function(src, docs) {
  325. 325 : var docIds = [];
  326. 326 : var corpus = this.getStore().getCorpus();
  327. 327 : docs.forEach(function(doc) {
  328. 328 : docIds.push(corpus.getDocument(doc).getId())
  329. 329 : }, this);
  330. 330 : this.setApiParams({docId: docIds, docIndex: undefined});
  331. 331 : this.loadFromApis();
  332. 332 : }
  333. 333 : }
  334. 334 : });
  335. 335 :
  336. 336 : me.callParent(arguments);
  337. 337 :
  338. 338 : me.getStore().getProxy().setExtraParam("withDistributions", true);
  339. 339 :
  340. 340 : }
  341. 341 :
  342. 342 : })