- 1 :
/**
- 2 :
* The Documents tool shows a table of the documents in the corpus and includes functionality for modifying the corpus.
- 3 :
*
- 4 :
* @example
- 5 :
*
- 6 :
* let config = {
- 7 :
* "columns": null,
- 8 :
* "dir": null,
- 9 :
* "docId": null,
- 10 :
* "docIndex": null,
- 11 :
* "query": null,
- 12 :
* "sort": null,
- 13 :
* };
- 14 :
*
- 15 :
* loadCorpus("austen").tool("documents", config);
- 16 :
*
- 17 :
* @class Documents
- 18 :
* @tutorial documents
- 19 :
* @memberof Tools
- 20 :
*/
- 21 :
Ext.define('Voyant.panel.Documents', {
- 22 :
extend: 'Ext.grid.Panel',
- 23 :
mixins: ['Voyant.panel.Panel','Voyant.util.Downloadable'],
- 24 :
alias: 'widget.documents',
- 25 :
isConsumptive: true,
- 26 :
statics: {
- 27 :
i18n: {
- 28 :
newCorpusError: 'There was an error creating the new the corpus. You may not have permission to do this.'
- 29 :
},
- 30 :
api: {
- 31 :
/**
- 32 :
* @memberof Tools.Documents
- 33 :
* @instance
- 34 :
* @property {query}
- 35 :
*/
- 36 :
query: undefined,
- 37 :
- 38 :
/**
- 39 :
* @memberof Tools.Documents
- 40 :
* @instance
- 41 :
* @property {docIndex}
- 42 :
*/
- 43 :
docIndex: undefined,
- 44 :
- 45 :
/**
- 46 :
* @memberof Tools.Documents
- 47 :
* @instance
- 48 :
* @property {docId}
- 49 :
*/
- 50 :
docId: undefined,
- 51 :
- 52 :
/**
- 53 :
* @memberof Tools.Documents
- 54 :
* @instance
- 55 :
* @property {columns} columns 'title', 'author', 'pubDate', 'publisher', 'pubPlace', 'keyword', 'collection', 'tokensCount-lexical', 'typesCount-lexical', 'typeTokenRatio-lexical', 'averageWordsPerSentence', 'language'
- 56 :
*/
- 57 :
columns: undefined,
- 58 :
- 59 :
/**
- 60 :
* @memberof Tools.Documents
- 61 :
* @instance
- 62 :
* @property {sort}
- 63 :
*/
- 64 :
sort: undefined,
- 65 :
- 66 :
/**
- 67 :
* @memberof Tools.Documents
- 68 :
* @instance
- 69 :
* @property {dir}
- 70 :
*/
- 71 :
dir: undefined
- 72 :
},
- 73 :
glyph: 'xf0ce@FontAwesome'
- 74 :
},
- 75 :
- 76 :
MODE_EDITING: 'editing',
- 77 :
MODE_NORMAL: 'normal',
- 78 :
config: {
- 79 :
options: [{
- 80 :
xtype: 'stoplistoption'
- 81 :
},
- 82 :
{xtype: 'categoriesoption'}
- 83 :
],
- 84 :
mode: this.MODE_NORMAL
- 85 :
},
- 86 :
- 87 :
constructor: function(config) {
- 88 :
- 89 :
var store = Ext.create("Voyant.data.store.Documents", {
- 90 :
selModel: {pruneRemoved: false},
- 91 :
proxy: {
- 92 :
extraParams: {
- 93 :
forTool: 'documents'
- 94 :
}
- 95 :
}
- 96 :
});
- 97 :
- 98 :
var dockedItemsItems = [{
- 99 :
xtype: 'querysearchfield'
- 100 :
}, {
- 101 :
xtype: 'totalpropertystatus'
- 102 :
}]
- 103 :
- 104 :
var me = this;
- 105 :
- 106 :
if (!config || config.mode!=this.MODE_EDITING) {
- 107 :
dockedItemsItems.push({
- 108 :
text: this.localize("modify"),
- 109 :
tooltip: this.localize("modifyTip"),
- 110 :
glyph: 'xf044@FontAwesome',
- 111 :
scope: this,
- 112 :
itemId: 'modifyButton',
- 113 :
handler: function(btn) {
- 114 :
var win = Ext.create('Ext.window.Window', {
- 115 :
title: this.localize("title"),
- 116 :
modal: true,
- 117 :
width: "80%",
- 118 :
minWidth: 300,
- 119 :
minHeight: 200,
- 120 :
height: "80%",
- 121 :
layout: 'fit',
- 122 :
frame: true,
- 123 :
border: true,
- 124 :
items: {
- 125 :
xtype: 'documents',
- 126 :
mode: this.MODE_EDITING,
- 127 :
corpus: this.getStore().getCorpus(),
- 128 :
header: false,
- 129 :
viewConfig: {
- 130 :
plugins:{
- 131 :
ptype:'gridviewdragdrop'
- 132 :
},
- 133 :
listeners: {
- 134 :
beforedrop: function(node, data, overModel, dropPosition, dropHandlers) {
- 135 :
if (this.getStore().getCount()<this.getStore().getCorpus().getDocumentsCount()) {
- 136 :
var panel = this.up("panel");
- 137 :
Ext.Msg.show({
- 138 :
title: panel.localize('error'),
- 139 :
message: panel.localize('reorderFilteredError'),
- 140 :
buttons: Ext.Msg.OK,
- 141 :
icon: Ext.Msg.ERROR
- 142 :
});
- 143 :
return false;
- 144 :
}
- 145 :
return true;
- 146 :
}
- 147 :
}
- 148 :
}
- 149 :
},
- 150 :
buttons: [{
- 151 :
text: this.localize('add'),
- 152 :
tooltip: this.localize("addTip"),
- 153 :
glyph: 'xf067@FontAwesome',
- 154 :
handler: function(btn) {
- 155 :
btn.up("window").close();
- 156 :
Ext.create('Ext.window.Window', {
- 157 :
header: false,
- 158 :
modal: true,
- 159 :
layout: 'fit',
- 160 :
items: {
- 161 :
xtype: 'corpuscreator',
- 162 :
corpus: this.getStore().getCorpus(),
- 163 :
listeners: {
- 164 :
boxready: function(cmp) {
- 165 :
cmp.setStyle({borderColor: '#f5f5f5'});
- 166 :
}
- 167 :
}
- 168 :
}
- 169 :
}).show();
- 170 :
},
- 171 :
scope: this
- 172 :
}, {
- 173 :
text: this.localize('remove'),
- 174 :
tooltip: this.localize("removeTip"),
- 175 :
glyph: 'xf05e@FontAwesome',
- 176 :
hidden: this.getStore().getCorpus().getDocumentsCount()==1,
- 177 :
handler: this.keepRemoveReorderHandler,
- 178 :
itemId: 'remove',
- 179 :
scope: this
- 180 :
}, {
- 181 :
text: this.localize('keep'),
- 182 :
tooltip: this.localize("keepTip"),
- 183 :
glyph: 'xf00c@FontAwesome',
- 184 :
hidden: this.getStore().getCorpus().getDocumentsCount()==1,
- 185 :
handler: this.keepRemoveReorderHandler,
- 186 :
itemId: 'keep',
- 187 :
scope: this
- 188 :
}, {
- 189 :
text: this.localize('reorder'),
- 190 :
tooltip: this.localize("reorderTip"),
- 191 :
glyph: 'xf0dc@FontAwesome',
- 192 :
hidden: this.getStore().getCorpus().getDocumentsCount()==1,
- 193 :
handler: this.keepRemoveReorderHandler,
- 194 :
itemId: 'reorder',
- 195 :
scope: this
- 196 :
},{
- 197 :
text: 'Cancel',
- 198 :
glyph: 'xf00d@FontAwesome',
- 199 :
handler: function(btn) {
- 200 :
btn.up("window").close();
- 201 :
}
- 202 :
}]
- 203 :
}).show();
- 204 :
- 205 :
}
- 206 :
}, {
- 207 :
text: this.localize('downloadButton'),
- 208 :
glyph: 'xf019@FontAwesome',
- 209 :
itemId: 'downloadButton',
- 210 :
handler: function() {
- 211 :
me.downloadFromCorpusId(me.getStore().getCorpus().getId())
- 212 :
}
- 213 :
})
- 214 :
}
- 215 :
- 216 :
Ext.apply(this, {
- 217 :
title: this.localize('title'),
- 218 :
emptyText: this.localize("emptyText"),
- 219 :
columns:[
- 220 :
{
- 221 :
xtype: 'rownumberer',
- 222 :
renderer: function(value, metaData, record) {return record.getIndex()+1},
- 223 :
sortable: false
- 224 :
},{
- 225 :
text: this.localize('documentTitle'),
- 226 :
dataIndex: 'title',
- 227 :
sortable: true,
- 228 :
renderer: function(val, metadata, record) {return record.getTitle();},
- 229 :
flex: 3
- 230 :
},{
- 231 :
text: this.localize('documentAuthor'),
- 232 :
dataIndex: 'author',
- 233 :
sortable: true,
- 234 :
hidden: true,
- 235 :
renderer: function(val, metadata, record) {return record.getAuthor();},
- 236 :
flex: 2
- 237 :
},{
- 238 :
text: this.localize('documentPubDate'),
- 239 :
dataIndex: 'pubDate',
- 240 :
sortable: true,
- 241 :
hidden: true,
- 242 :
renderer: function(val, metadata, record) {return record.getPubDate();},
- 243 :
flex: 2
- 244 :
},{
- 245 :
text: this.localize('documentPublisher'),
- 246 :
dataIndex: 'publisher',
- 247 :
sortable: false,
- 248 :
hidden: true,
- 249 :
renderer: function(val, metadata, record) {return record.getPublisher();},
- 250 :
flex: 2
- 251 :
},{
- 252 :
text: this.localize('documentPubPlace'),
- 253 :
dataIndex: 'pubPlace',
- 254 :
sortable: false,
- 255 :
hidden: true,
- 256 :
renderer: function(val, metadata, record) {return record.getPubPlace();},
- 257 :
flex: 2
- 258 :
},{
- 259 :
text: this.localize('documentKeyword'),
- 260 :
dataIndex: 'keyword',
- 261 :
sortable: false,
- 262 :
hidden: true,
- 263 :
renderer: function(val, metadata, record) {return record.getKeyword();},
- 264 :
flex: 2
- 265 :
},{
- 266 :
text: this.localize('documentCollection'),
- 267 :
dataIndex: 'collection',
- 268 :
sortable: false,
- 269 :
hidden: true,
- 270 :
renderer: function(val, metadata, record) {return record.getCollection();},
- 271 :
flex: 2
- 272 :
},{
- 273 :
text: this.localize('tokensCountLexical'),
- 274 :
dataIndex: 'tokensCount-lexical',
- 275 :
renderer: Ext.util.Format.numberRenderer('0,000'),
- 276 :
sortable: true,
- 277 :
width: 'autoSize'
- 278 :
},{
- 279 :
text: this.localize('typesCountLexical'),
- 280 :
dataIndex: 'typesCount-lexical',
- 281 :
renderer: Ext.util.Format.numberRenderer('0,000'),
- 282 :
width: 'autoSize'
- 283 :
},{
- 284 :
text: this.localize('typeTokenRatioLexical'),
- 285 :
dataIndex: 'typeTokenRatio-lexical',
- 286 :
renderer: function(val) {return Ext.util.Format.percent(val)},
- 287 :
width: 'autoSize'
- 288 :
},{
- 289 :
text: this.localize('averageWordsPerSentence'),
- 290 :
dataIndex: 'averageWordsPerSentence',
- 291 :
renderer: Ext.util.Format.numberRenderer('0,000.0'),
- 292 :
tooltip: this.localize("averageWordsPerSentenceTip"),
- 293 :
width: 'autoSize'
- 294 :
},{
- 295 :
text: this.localize('language'),
- 296 :
dataIndex: 'language',
- 297 :
hidden: true,
- 298 :
renderer: function(val, metaData, record, rowIndex, colIndex, store, view) {return view.ownerCt.getLanguage(val);},
- 299 :
width: 'autoSize'
- 300 :
}
- 301 :
],
- 302 :
- 303 :
store: store,
- 304 :
- 305 :
selModel: {
- 306 :
type: 'rowmodel',
- 307 :
mode: 'MULTI',
- 308 :
listeners: {
- 309 :
selectionchange: {
- 310 :
fn: function(sm, selections) {
- 311 :
this.getApplication().dispatchEvent('documentsClicked', this, selections, this.getStore().getCorpus());
- 312 :
},
- 313 :
scope: this
- 314 :
}
- 315 :
}
- 316 :
},
- 317 :
- 318 :
dockedItems: [{
- 319 :
dock: 'bottom',
- 320 :
xtype: 'toolbar',
- 321 :
overflowHandler: 'scroller',
- 322 :
items: dockedItemsItems
- 323 :
}]
- 324 :
});
- 325 :
- 326 :
this.callParent(arguments);
- 327 :
this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
- 328 :
- 329 :
// create a listener for corpus loading (defined here, in case we need to load it next)
- 330 :
this.on('loadedCorpus', function(src, corpus) {
- 331 :
- 332 :
this.store.setCorpus(corpus);
- 333 :
- 334 :
if (this.isVisible()) {
- 335 :
this.store.load({params: this.getApiParams()});
- 336 :
} else {
- 337 :
this.on('afterrender', function() {
- 338 :
this.store.load({params: this.getApiParams()});
- 339 :
}, this);
- 340 :
}
- 341 :
- 342 :
if (this.hasModifyCorpusAccess(corpus) === false) {
- 343 :
this.queryById('modifyButton').hide();
- 344 :
this.queryById('downloadButton').hide();
- 345 :
}
- 346 :
});
- 347 :
- 348 :
this.on("activate", function() { // load after tab activate (if we're in a tab panel)
- 349 :
if (this.getStore().getCorpus()) {
- 350 :
this.getStore().load({params: this.getApiParams()});
- 351 :
}
- 352 :
}, this);
- 353 :
- 354 :
// create a listener for corpus loading (defined here, in case we need to load it next)
- 355 :
this.on('query', function(src, query) {
- 356 :
this.setApiParam('query', query);
- 357 :
this.store.load({params: this.getApiParams()});
- 358 :
})
- 359 :
- 360 :
if (config.embedded) {
- 361 :
if (Ext.getClass(config.embedded).getName() == "Voyant.data.model.Corpus") {
- 362 :
config.corpus = config.embedded
- 363 :
}
- 364 :
else if (Ext.getClass(config.embedded).getName() == "Voyant.data.store.Documents") {
- 365 :
this.store.setRecords(config.embedded.getData())
- 366 :
config.corpus = config.embedded.getCorpus()
- 367 :
}
- 368 :
- 369 :
}
- 370 :
- 371 :
// if we have a corpus, load it
- 372 :
if (config.corpus) {
- 373 :
this.fireEvent('loadedCorpus', this, config.corpus)
- 374 :
}
- 375 :
},
- 376 :
- 377 :
keepRemoveReorderHandler: function(btn) {
- 378 :
// we're not sure which scope we're in, so ensure we're talking about this buttons panel
- 379 :
var panel = btn.up("window").down("documents");
- 380 :
var selection = panel.getSelection();
- 381 :
var docs = panel.getStore().getCorpus().getDocumentsCount();
- 382 :
var btnMode = btn.getItemId();
- 383 :
// if reordering, check to make sure that we're not looking at a subset
- 384 :
if (btnMode=='reorder') {
- 385 :
if (panel.getStore().getCount()<docs) {
- 386 :
return Ext.Msg.show({
- 387 :
title: this.localize('error'),
- 388 :
message: this.localize('reorderFilteredError'),
- 389 :
buttons: Ext.Msg.OK,
- 390 :
icon: Ext.Msg.ERROR
- 391 :
});
- 392 :
}
- 393 :
else {
- 394 :
docIndex = [];
- 395 :
panel.getStore().each(function(doc) {
- 396 :
docIndex.push(doc.getIndex())
- 397 :
}, this);
- 398 :
for (var i=1; i<docIndex.length; i++) {
- 399 :
if (docIndex[i-1]>docIndex[i]) {
- 400 :
return Ext.Msg.confirm(panel.localize('newCorpus'), new Ext.Template(panel.localize(btnMode+'Documents')).applyTemplate([selection.length]), function(confirmBtn){
- 401 :
if (confirmBtn==='yes') {
- 402 :
docIndex = [];
- 403 :
this.getStore().each(function(doc) {
- 404 :
docIndex.push(doc.getIndex())
- 405 :
}, this);
- 406 :
var params = {docIndex: docIndex};
- 407 :
params[btnMode+"Documents"] = true;
- 408 :
this.editCorpus(params)
- 409 :
}
- 410 :
}, panel);
- 411 :
}
- 412 :
}
- 413 :
// if we get here it's because nothing's been reordered
- 414 :
return Ext.Msg.show({
- 415 :
title: this.localize('error'),
- 416 :
message: this.localize('reorderOriginalError'),
- 417 :
buttons: Ext.Msg.OK,
- 418 :
icon: Ext.Msg.ERROR
- 419 :
});
- 420 :
}
- 421 :
- 422 :
}
- 423 :
- 424 :
if (selection.length>0) {
- 425 :
if (selection.length==docs) {
- 426 :
if (docs==1) {
- 427 :
return Ext.Msg.show({
- 428 :
title: this.localize('error'),
- 429 :
message: this.localize('onlyOneError'),
- 430 :
buttons: Ext.Msg.OK,
- 431 :
icon: Ext.Msg.ERROR
- 432 :
});
- 433 :
}
- 434 :
else {
- 435 :
return Ext.Msg.show({
- 436 :
title: this.localize('error'),
- 437 :
message: this.localize('allSelectedError'),
- 438 :
buttons: Ext.Msg.OK,
- 439 :
icon: Ext.Msg.ERROR
- 440 :
});
- 441 :
}
- 442 :
}
- 443 :
else {
- 444 :
return Ext.Msg.confirm(this.localize('newCorpus'), new Ext.Template(this.localize(btnMode+'SelectedDocuments')).applyTemplate([selection.length]), function(confirmBtn){
- 445 :
if (confirmBtn==='yes') {
- 446 :
docIndex = [];
- 447 :
selection.forEach(function(doc){
- 448 :
docIndex.push(doc.getIndex())
- 449 :
})
- 450 :
var params = {docIndex: docIndex};
- 451 :
params[btnMode+"Documents"] = true;
- 452 :
this.editCorpus(params)
- 453 :
}
- 454 :
}, panel);
- 455 :
}
- 456 :
}
- 457 :
else if (panel.getApiParam("query") && panel.getStore().getCount()<docs) {
- 458 :
return Ext.Msg.confirm(this.localize('newCorpus'), new Ext.Template(this.localize(btnMode+'FilteredDocuments')).applyTemplate([selection.length]), function(confirmBtn){
- 459 :
if (confirmBtn==='yes') {
- 460 :
docIndex = [];
- 461 :
this.getStore().each(function(doc) {
- 462 :
docIndex.push(doc.getIndex())
- 463 :
}, this);
- 464 :
var params = {docIndex: docIndex};
- 465 :
params[btnMode+"Documents"] = true;
- 466 :
this.editCorpus(params)
- 467 :
}
- 468 :
}, panel);
- 469 :
}
- 470 :
else {
- 471 :
return Ext.Msg.show({
- 472 :
title: this.localize('error'),
- 473 :
message: this.localize('selectOrFilterError'),
- 474 :
buttons: Ext.Msg.OK,
- 475 :
icon: Ext.Msg.ERROR
- 476 :
});
- 477 :
}
- 478 :
},
- 479 :
- 480 :
editCorpus: function(params) {
- 481 :
- 482 :
Ext.apply(params, {
- 483 :
tool: 'corpus.CorpusManager',
- 484 :
corpus: this.getStore().getCorpus().getId()
- 485 :
})
- 486 :
- 487 :
// mask main viewport while we create a new corpus
- 488 :
var app = this.getApplication();
- 489 :
var view = app.getViewport();
- 490 :
view.mask(this.localize("Creating new corpus…"));
- 491 :
Ext.Ajax.request({
- 492 :
url: this.getApplication().getTromboneUrl(),
- 493 :
method: 'POST',
- 494 :
params: params,
- 495 :
success: function(response) {
- 496 :
view.unmask();
- 497 :
var obj = Ext.decode(response.responseText);
- 498 :
app.openUrl(app.getBaseUrl()+"?corpus="+obj.corpus.id);
- 499 :
// view.mask("Loading new corpus…")
- 500 :
// new Voyant.data.model.Corpus({corpus: obj.corpus.id}).then(function(corpus) {
- 501 :
// view.unmask();
- 502 :
// app.openUrl(app.getBaseUrl()+"/?corpus="+obj.corpus.id)
- 503 :
// app.dispatchEvent('loadedCorpus', app, corpus);
- 504 :
// }).fail(function(message, response) {
- 505 :
// view.unmask();
- 506 :
// app.showErrorResponse({message: message}, response);
- 507 :
// });
- 508 :
},
- 509 :
failure: function(response) {
- 510 :
view.unmask();
- 511 :
Ext.Msg.show({
- 512 :
title: this.localize('error'),
- 513 :
message: this.localize('newCorpusError'),
- 514 :
buttons: Ext.Msg.OK,
- 515 :
icon: Ext.Msg.ERROR
- 516 :
});
- 517 :
},
- 518 :
scope: this
- 519 :
});
- 520 :
- 521 :
// close editing window if we're in modal mode, should happen asynchronously while new corpus is created
- 522 :
var win = this.up("window");
- 523 :
if (win && win.isFloating()) {win.close()}
- 524 :
}
- 525 :
})