/**
* The Contexts (or Keywords in Context) tool shows each occurrence of a keyword with a bit of surrounding text (the context).
*
* @example
*
* let config = {
* columns: null,
* context: 5,
* dir: null,
* docId: null,
* docIndex: null,
* expand: null,
* query: null,
* sort: null,
* stopList: null,
* termColors: null
* };
*
* loadCorpus("austen").tool("Contexts", config);
*
* @class Contexts
* @tutorial contexts
* @memberof Tools
*/
Ext.define('Voyant.panel.Contexts', {
extend: 'Ext.grid.Panel',
mixins: ['Voyant.panel.Panel'],
requires: ['Voyant.data.store.Contexts'],
alias: 'widget.contexts',
isConsumptive: true,
statics: {
i18n: {
},
api: {
/**
* @memberof Tools.Contexts
* @instance
* @property {query}
*/
query: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {docId}
*/
docId: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {docIndex}
*/
docIndex: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {stopList}
* @default
*/
stopList: 'auto',
/**
* @memberof Tools.Contexts
* @instance
* @property {context}
* @default
*/
context: 5,
/**
* @memberof Tools.Contexts
* @instance
* @property {Number} expand How many terms to show when you expand any given row
* @default
*/
expand: 50,
/**
* @memberof Tools.Contexts
* @instance
* @property {columns} columns 'docIndex', 'left', 'term', 'right', 'position'
*/
columns: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {sort}
*/
sort: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {dir}
*/
dir: undefined,
/**
* @memberof Tools.Contexts
* @instance
* @property {termColors}
* @default
*/
termColors: 'categories'
},
glyph: 'xf0ce@FontAwesome'
},
config: {
options: [{xtype: 'stoplistoption'},{xtype: 'categoriesoption'},{xtype: 'termcolorsoption'}]
},
constructor: function() {
this.mixins['Voyant.util.Api'].constructor.apply(this, arguments);
this.callParent(arguments);
this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
},
initComponent: function() {
var me = this;
Ext.apply(me, {
title: this.localize('title'),
emptyText: this.localize("emptyText"),
store : Ext.create("Voyant.data.store.ContextsBuffered", {
parentPanel: this,
proxy: {
extraParams: {
stripTags: "all"
}
}
// sortOnLoad: true,
// sorters: {
// property: 'position',
// direction: 'ASC'
// }
}),
selModel: {
type: 'rowmodel',
listeners: {
selectionchange: {
fn: function(sm, selections) {
this.getApplication().dispatchEvent('termLocationClicked', this, selections);
},
scope: this
}
}
},
plugins: [{ // the expander slider assumes there's only one plugin, needs to be updated if changed
ptype: 'rowexpander',
rowBodyTpl : new Ext.XTemplate('')
}],
dockedItems: [{
dock: 'bottom',
xtype: 'toolbar',
overflowHandler: 'scroller',
items: [{
xtype: 'querysearchfield'
}, {
xtype: 'totalpropertystatus'
}, this.localize('context'), {
xtype: 'slider',
minValue: 5,
value: 5,
maxValue: 50,
increment: 5,
width: 50,
listeners: {
render: function(slider) {
slider.setValue(me.getApiParam('context'));
},
changecomplete: function(slider, newValue) {
me.setApiParam("context", slider.getValue());
me.getStore().clearAndLoad({params: me.getApiParams()});
}
}
}, this.localize('expand'), {
xtype: 'slider',
minValue: 5,
value: 5,
maxValue: 500,
increment: 10,
width: 50,
listeners: {
render: function(slider) {
slider.setValue(me.getApiParam('expand'));
},
changecomplete: function(slider, newValue) {
me.setApiParam('expand', newValue);
var view = me.getView();
var recordsExpanded = me.plugins[0].recordsExpanded;
var store = view.getStore();
for (var id in recordsExpanded) {
var record = store.getByInternalId(id);
var row = view.getRow(record);
var expandRow = row.parentNode.childNodes[1];
if (recordsExpanded[id]) {
view.fireEvent("expandbody", row, record, expandRow, {force: true});
} else {
Ext.fly(expandRow).down('.x-grid-rowbody').setHtml('');
}
}
}
}
},{
xtype: 'corpusdocumentselector'
}]
}],
columns: [{
text: this.localize("document"),
tooltip: this.localize("documentTip"),
width: 'autoSize',
dataIndex: 'docIndex',
sortable: true,
renderer: function (value, metaData, record, rowIndex, colIndex, store) {
return store.getCorpus().getDocument(value).getTitle();
}
},{
text: this.localize("left"),
tooltip: this.localize("leftTip"),
align: 'right',
dataIndex: 'left',
sortable: true,
flex: 1
},{
text: this.localize("term"),
tooltip: this.localize("termTip"),
dataIndex: 'term',
sortable: true,
width: 'autoSize',
xtype: 'coloredtermfield'
},{
text: this.localize("right"),
tooltip: this.localize("rightTip"),
dataIndex: 'right',
sortable: true,
flex: 1
},{
text: this.localize("position"),
tooltip: this.localize("positionTip"),
dataIndex: 'position',
sortable: true,
hidden: true,
flex: 1
}],
listeners: {
scope: this,
corpusSelected: function() {
if (this.getStore().getCorpus()) {
this.setApiParams({docId: undefined, docIndex: undefined})
this.getStore().clearAndLoad()
}
},
documentsSelected: function(src, docs) {
var docIds = [];
var corpus = this.getStore().getCorpus();
docs.forEach(function(doc) {
docIds.push(corpus.getDocument(doc).getId())
}, this);
this.setApiParams({docId: docIds, docIndex: undefined})
this.getStore().clearAndLoad()
},
documentSegmentTermClicked: {
fn: function(src, documentSegmentTerm) {
if (!documentSegmentTerm.term) {return;}
params = {query: documentSegmentTerm.term};
if (documentSegmentTerm.docId) {
params.docId = documentSegmentTerm.docId;
}
else {
// default to first document
params.docIndex = documentSegmentTerm.docIndex ? documentSegmentTerm.docIndex : 0;
}
this.setApiParams(params);
if (this.isVisible()) {
this.getStore().clearAndLoad()
}
},
scope: this
},
documentIndexTermsClicked: {
fn: function(src, documentIndexTerms) {
// this isn't quite right, since we want every term associated with a docIndex, but for now it will do
var queriesHash = {};
var queries = [];
var docIndexHash = {};
var docIndex = [];
documentIndexTerms.forEach(function(item) {
if (!queriesHash[item.term]) {
queries.push(item.term);
queriesHash[item.term]=true;
}
if (!docIndexHash[item.docIndex]) {
docIndex.push(item.docIndex);
docIndexHash[item.docIndex]=true;
}
});
this.setApiParams({
docId: undefined,
docIndex: docIndex,
query: queries
});
if (this.isVisible()) {
this.getStore().clearAndLoad({params: this.getApiParams()});
}
},
scope: this
},
afterrender: function(me) {
me.getView().on('expandbody', function( rowNode, record, expandRow, eOpts ) {
if (expandRow.textContent==="" || (eOpts && eOpts.force)) {
var store = Ext.create("Voyant.data.store.Contexts", {
stripTags: "all",
corpus: me.getStore().getCorpus()
});
var data = record.getData();
var query = data.query;
if (query.match(/^[\^@]/) !== null) {
query = data.term; // if it's a category query then use term instead
}
store.load({
params: {
query: query,
docIndex: data.docIndex,
position: data.position,
limit: 1,
context: me.getApiParam('expand')
},
callback: function(records, operation, success) {
if (success && records.length==1) {
data = records[0].getData();
Ext.fly(operation.expandRow).down('.x-grid-rowbody').setHtml(data.left + " <span class='word keyword'>" + data.middle + "</span> " + data.right);
}
},
expandRow: expandRow
});
}
});
}
}
});
me.on("loadedCorpus", function(src, corpus) {
if (this.hasCorpusAccess(corpus)==false) {
this.mask(this.localize('limitedAccess'), 'mask-no-spinner');
}
else {
var query = Ext.Array.from(this.getApiParam("query"));
if (query.length > 0 && query[0].match(/^[\^@]/) !== null) {
// query is a category so just load
this.getStore().clearAndLoad({params: this.getApiParams()});
} else {
var corpusTerms = corpus.getCorpusTerms({autoLoad: false});
corpusTerms.load({
callback: function(records, operation, success) {
if (success && records.length>0) {
this.setApiParam("query", [records[0].getTerm()]);
this.getStore().clearAndLoad({params: this.getApiParams()});
}
},
scope: me,
params: {
limit: 1,
query: query,
stopList: this.getApiParam("stopList"),
forTool: 'contexts'
}
});
}
}
});
me.on("query", function(src, query) {
this.setApiParam('query', query);
this.getStore().clearAndLoad({params: this.getApiParams()});
}, me);
me.on("documentTermsClicked", function(src, documentTerms) {
var documentIndexTerms = [];
documentTerms.forEach(function(documentTerm) {
documentIndexTerms.push({
term: documentTerm.getTerm(),
docIndex: documentTerm.getDocIndex()
});
});
this.fireEvent("documentIndexTermsClicked", this, documentIndexTerms);
});
me.on("termsClicked", function(src, terms) {
var documentIndexTerms = [];
if (Ext.isString(terms)) {terms = [terms];}
terms.forEach(function(term) {
if (term.docIndex !== undefined) {
documentIndexTerms.push({
term: term.term,
docIndex: term.docIndex
});
}
});
if (documentIndexTerms.length > 0) {
this.fireEvent("documentIndexTermsClicked", this, documentIndexTerms);
}
});
me.callParent(arguments);
}
});