- 1 :
/**
- 2 :
* The Phrases tool shows repeating sequences of words organized by frequency of repetition or number of words in each repeated phrase.
- 3 :
*
- 4 :
* @example
- 5 :
*
- 6 :
* let config = {
- 7 :
* "columns": null,
- 8 :
* "dir": null,
- 9 :
* "docId": null,
- 10 :
* "docIndex": null,
- 11 :
* "maxLength": null,
- 12 :
* "minLength": null,
- 13 :
* "overlapFilter": null,
- 14 :
* "query": null,
- 15 :
* "sort": null,
- 16 :
* "stopList": null
- 17 :
* };
- 18 :
*
- 19 :
* loadCorpus("austen").tool("phrases", config);
- 20 :
*
- 21 :
* @class Phrases
- 22 :
* @tutorial phrases
- 23 :
* @memberof Tools
- 24 :
*/
- 25 :
Ext.define('Voyant.panel.Phrases', {
- 26 :
extend: 'Ext.grid.Panel',
- 27 :
mixins: ['Voyant.panel.Panel'],
- 28 :
alias: 'widget.phrases',
- 29 :
isConsumptive: true,
- 30 :
statics: {
- 31 :
i18n: {
- 32 :
},
- 33 :
api: {
- 34 :
/**
- 35 :
* @memberof Tools.Phrases
- 36 :
* @instance
- 37 :
* @property {stopList}
- 38 :
* @default
- 39 :
*/
- 40 :
stopList: 'auto',
- 41 :
- 42 :
/**
- 43 :
* @memberof Tools.Phrases
- 44 :
* @instance
- 45 :
* @property {query}
- 46 :
*/
- 47 :
query: undefined,
- 48 :
- 49 :
/**
- 50 :
* @memberof Tools.Phrases
- 51 :
* @instance
- 52 :
* @property {docId}
- 53 :
*/
- 54 :
docId: undefined,
- 55 :
- 56 :
/**
- 57 :
* @memberof Tools.Phrases
- 58 :
* @instance
- 59 :
* @property {docIndex}
- 60 :
*/
- 61 :
docIndex: undefined,
- 62 :
- 63 :
/**
- 64 :
* @memberof Tools.Phrases
- 65 :
* @instance
- 66 :
* @property {Number} minLength The minimum length (number of words) of the phrase to consider.
- 67 :
* @default
- 68 :
*/
- 69 :
minLength: 2,
- 70 :
- 71 :
/**
- 72 :
* @memberof Tools.Phrases
- 73 :
* @instance
- 74 :
* @property {Number} maxLength The maximum length (number of words) of the phrase to consider.
- 75 :
* @default
- 76 :
*/
- 77 :
maxLength: 50,
- 78 :
- 79 :
/**
- 80 :
* @memberof Tools.Phrases
- 81 :
* @instance
- 82 :
* @property {String} overlapFilter Specifies the strategory for prioritizing and filtering out phrases. Options are: 'none' (no filtering), 'length' (prioritize phrase length), or 'rawFreq' (prioritize phrase frequency). See [Phrases options](tutorial-phrases.html#options) for more info.
- 83 :
* @default
- 84 :
*/
- 85 :
overlapFilter: 'length',
- 86 :
- 87 :
/**
- 88 :
* @memberof Tools.Phrases
- 89 :
* @instance
- 90 :
* @property {columns} columns 'term', 'rawFreq', 'length', 'distributions'
- 91 :
*/
- 92 :
columns: undefined,
- 93 :
- 94 :
/**
- 95 :
* @memberof Tools.Phrases
- 96 :
* @instance
- 97 :
* @property {sort}
- 98 :
* @default
- 99 :
*/
- 100 :
sort: 'length',
- 101 :
- 102 :
/**
- 103 :
* @memberof Tools.Phrases
- 104 :
* @instance
- 105 :
* @property {dir}
- 106 :
* @default
- 107 :
*/
- 108 :
dir: 'desc'
- 109 :
},
- 110 :
glyph: 'xf0ce@FontAwesome'
- 111 :
},
- 112 :
config: {
- 113 :
/**
- 114 :
* @private
- 115 :
*/
- 116 :
options: [{xtype: 'stoplistoption'},{xtype: 'categoriesoption'}],
- 117 :
},
- 118 :
constructor: function(config) {
- 119 :
- 120 :
this.callParent(arguments);
- 121 :
this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
- 122 :
- 123 :
// create a listener for corpus loading (defined here, in case we need to load it next)
- 124 :
this.on('loadedCorpus', function(src, corpus) {
- 125 :
if (this.isVisible()) {
- 126 :
if (this.hasCorpusAccess(corpus)==false) {
- 127 :
this.mask(this.localize('limitedAccess'), 'mask-no-spinner');
- 128 :
} else {
- 129 :
this.loadFromApis();
- 130 :
}
- 131 :
}
- 132 :
- 133 :
});
- 134 :
- 135 :
if (config.embedded) {
- 136 :
// var cls = Ext.getClass(config.embedded).getName();
- 137 :
// if (cls=="Voyant.data.store.DocumentTerms" || cls=="Voyant.data.model.Document") {
- 138 :
// this.fireEvent('loadedCorpus', this, config.embedded.getCorpus())
- 139 :
// }
- 140 :
}
- 141 :
else if (config.corpus) {
- 142 :
this.fireEvent('loadedCorpus', this, config.corpus)
- 143 :
}
- 144 :
- 145 :
this.on("corpusTermsClicked", function(src, terms) {
- 146 :
if (this.getStore().getCorpus()) { // make sure we have a corpus
- 147 :
var query = [];
- 148 :
terms.forEach(function(term) {
- 149 :
query.push(term.get("term"));
- 150 :
})
- 151 :
this.setApiParams({
- 152 :
query: query,
- 153 :
docId: undefined,
- 154 :
docIndex: undefined
- 155 :
});
- 156 :
if (this.isVisible()) {
- 157 :
this.getStore().load({params: this.getApiParams()});
- 158 :
}
- 159 :
}
- 160 :
});
- 161 :
- 162 :
this.on("activate", function() { // load after tab activate (if we're in a tab panel)
- 163 :
if (this.getStore().getCorpus()) {this.loadFromApis()}
- 164 :
}, this)
- 165 :
- 166 :
this.on("query", function(src, query) {
- 167 :
this.setApiParam("query", query);
- 168 :
this.getStore().getProxy().setExtraParam("query", query);
- 169 :
this.loadFromApis();
- 170 :
}, this)
- 171 :
},
- 172 :
- 173 :
loadFromApis: function() {
- 174 :
if (this.getStore().getCorpus()) {
- 175 :
this.getStore().load({params: this.getApiParams()});
- 176 :
}
- 177 :
},
- 178 :
- 179 :
initComponent: function() {
- 180 :
var me = this;
- 181 :
- 182 :
var store = Ext.create("Voyant.data.store.CorpusNgramsBuffered", {
- 183 :
parentPanel: me,
- 184 :
leadingBufferZone: 100 // since these calls are expensive reduce buffer to 1 page
- 185 :
});
- 186 :
- 187 :
store.on("beforeload", function(store) {
- 188 :
return me.hasCorpusAccess(store.getCorpus());
- 189 :
});
- 190 :
me.on("sortchange", function( ct, column, direction, eOpts ) {
- 191 :
this.setApiParam('sort', column.dataIndex);
- 192 :
this.setApiParam('dir', direction);
- 193 :
var api = this.getApiParams(["stopList", "query", "docId", "docIndex", "sort", "dir", "minLength", "maxLength", "overlapFilter"]);
- 194 :
var proxy = this.getStore().getProxy();
- 195 :
for (var key in api) {proxy.setExtraParam(key, api[key]);}
- 196 :
}, me)
- 197 :
- 198 :
Ext.apply(me, {
- 199 :
title: this.localize('title'),
- 200 :
emptyText: this.localize("emptyText"),
- 201 :
store : store,
- 202 :
selModel: Ext.create('Ext.selection.CheckboxModel', {
- 203 :
listeners: {
- 204 :
selectionchange: {
- 205 :
fn: function(sm, selections) {
- 206 :
if (selections.length > 0) {
- 207 :
var terms = [];
- 208 :
selections.forEach(function(selection) {
- 209 :
terms.push('"'+selection.getTerm()+'"')
- 210 :
})
- 211 :
this.getApplication().dispatchEvent('termsClicked', this, terms);
- 212 :
}
- 213 :
},
- 214 :
scope: this
- 215 :
}
- 216 :
}
- 217 :
}),
- 218 :
dockedItems: [{
- 219 :
dock: 'bottom',
- 220 :
xtype: 'toolbar',
- 221 :
overflowHandler: 'scroller',
- 222 :
items: [{
- 223 :
xtype: 'querysearchfield'
- 224 :
}, {
- 225 :
xtype: 'totalpropertystatus'
- 226 :
}, '-', {
- 227 :
text: me.localize('length'),
- 228 :
tooltip: 'test',
- 229 :
xtype: 'label'
- 230 :
}, {
- 231 :
xtype: 'slider',
- 232 :
minValue: 2,
- 233 :
values: [2, 50],
- 234 :
maxValue: 50,
- 235 :
increment: 1,
- 236 :
width: 75,
- 237 :
tooltip: this.localize("lengthTip"),
- 238 :
listeners: {
- 239 :
render: {
- 240 :
fn: function(slider) {
- 241 :
var values = slider.getValues();
- 242 :
slider.setValue(0, parseInt(this.getApiParam("minLength", values[0])))
- 243 :
slider.setValue(1, parseInt(this.getApiParam("maxLength", values[1])))
- 244 :
},
- 245 :
scope: me
- 246 :
},
- 247 :
changecomplete: {
- 248 :
fn: function(slider, newValue) {
- 249 :
var values = slider.getValues();
- 250 :
this.setApiParam("minLength", parseInt(values[0]));
- 251 :
this.setApiParam("maxLength", parseInt(values[1]));
- 252 :
this.getStore().load({params: this.getApiParams()});
- 253 :
},
- 254 :
scope: me
- 255 :
}
- 256 :
}
- 257 :
}, {
- 258 :
xtype: 'corpusdocumentselector'
- 259 :
}, '-', {
- 260 :
xtype: 'button',
- 261 :
text: this.localize('overlap'),
- 262 :
tooltip: this.localize('overlapTip'),
- 263 :
menu: {
- 264 :
items: [
- 265 :
{
- 266 :
xtype: 'menucheckitem',
- 267 :
text: this.localize("overlapNone"),
- 268 :
group: 'overlap',
- 269 :
inputValue: 'none',
- 270 :
checkHandler: function() {
- 271 :
this.setApiParam('overlapFilter', 'none')
- 272 :
this.getStore().load({params: this.getApiParams()})
- 273 :
},
- 274 :
scope: this
- 275 :
}, {
- 276 :
xtype: 'menucheckitem',
- 277 :
text: this.localize("overlapLength"),
- 278 :
group: 'overlap',
- 279 :
inputValue: 'length',
- 280 :
checkHandler: function() {
- 281 :
this.setApiParam('overlapFilter', 'length')
- 282 :
this.getStore().load({params: this.getApiParams()})
- 283 :
},
- 284 :
scope: this
- 285 :
}, {
- 286 :
xtype: 'menucheckitem',
- 287 :
text: this.localize("overlapFreq"),
- 288 :
group: 'overlap',
- 289 :
inputValue: 'rawFreq',
- 290 :
checkHandler: function() {
- 291 :
this.setApiParam('overlapFilter', 'rawFreq')
- 292 :
this.getStore().load({params: this.getApiParams()})
- 293 :
},
- 294 :
scope: this
- 295 :
}
- 296 :
],
- 297 :
listeners: {
- 298 :
afterrender: {
- 299 :
fn: function(menu) {
- 300 :
var overlapFilter = this.getApiParam('overlapFilter');
- 301 :
menu.items.each(function(item) {
- 302 :
if (item.group) {
- 303 :
item.setChecked(item.inputValue==overlapFilter);
- 304 :
}
- 305 :
}, this)
- 306 :
},
- 307 :
scope: this
- 308 :
}
- 309 :
- 310 :
}
- 311 :
}
- 312 :
}]
- 313 :
}],
- 314 :
columns: [{
- 315 :
text: this.localize("term"),
- 316 :
dataIndex: 'term',
- 317 :
tooltip: this.localize("termTip"),
- 318 :
sortable: true,
- 319 :
flex: 1
- 320 :
},{
- 321 :
text: this.localize("rawFreq"),
- 322 :
dataIndex: 'rawFreq',
- 323 :
tooltip: this.localize("termRawFreqTip"),
- 324 :
sortable: true,
- 325 :
width: 'autoSize'
- 326 :
},{
- 327 :
text: this.localize("length"),
- 328 :
dataIndex: 'length',
- 329 :
tooltip: this.localize("lengthTip"),
- 330 :
sortable: true,
- 331 :
width: 'autoSize'
- 332 :
},{
- 333 :
xtype: 'widgetcolumn',
- 334 :
text: this.localize("trend"),
- 335 :
tooltip: this.localize('trendTip'),
- 336 :
width: 120,
- 337 :
dataIndex: 'distributions',
- 338 :
widget: {
- 339 :
xtype: 'sparklineline'
- 340 :
}
- 341 :
}],
- 342 :
- 343 :
listeners: {
- 344 :
corpusSelected: function() {
- 345 :
this.setApiParams({docIndex: undefined, docId: undefined});
- 346 :
this.loadFromApis();
- 347 :
},
- 348 :
documentsSelected: function(src, docs) {
- 349 :
var docIds = [];
- 350 :
var corpus = this.getStore().getCorpus();
- 351 :
docs.forEach(function(doc) {
- 352 :
docIds.push(corpus.getDocument(doc).getId())
- 353 :
}, this);
- 354 :
this.setApiParams({docId: docIds, docIndex: undefined})
- 355 :
this.loadFromApis();
- 356 :
},
- 357 :
termsClicked: {
- 358 :
fn: function(src, terms) {
- 359 :
if (this.getStore().getCorpus()) { // make sure we have a corpus
- 360 :
var queryTerms = [];
- 361 :
terms.forEach(function(term) {
- 362 :
if (Ext.isString(term)) {queryTerms.push(term);}
- 363 :
else if (term.term) {queryTerms.push(term.term);}
- 364 :
else if (term.getTerm) {queryTerms.push(term.getTerm());}
- 365 :
});
- 366 :
if (queryTerms.length > 0) {
- 367 :
this.setApiParams({
- 368 :
docIndex: undefined,
- 369 :
docId: undefined,
- 370 :
query: queryTerms
- 371 :
});
- 372 :
if (this.isVisible()) {
- 373 :
if (this.isVisible()) {
- 374 :
this.getStore().clearAndLoad({params: this.getApiParams()});
- 375 :
}
- 376 :
}
- 377 :
}
- 378 :
}
- 379 :
},
- 380 :
scope: this
- 381 :
}
- 382 :
}
- 383 :
});
- 384 :
- 385 :
me.callParent(arguments);
- 386 :
- 387 :
me.getStore().getProxy().setExtraParam("withDistributions", true);
- 388 :
- 389 :
}
- 390 :
- 391 :
})