|
|
|
const maxSelectedFrames = 3; |
|
|
|
|
|
const SocioFillmoreDemo = { |
|
data() { |
|
return { |
|
interactiveModeEnabled: false, |
|
showIntroductionBox: true, |
|
showExplanationText: true, |
|
|
|
|
|
selectedExampleId: -1, |
|
|
|
|
|
eventTypeName: "", |
|
eventTypeDescr: "", |
|
eventTypeTextExample: "", |
|
|
|
|
|
primaryFrames: Array(maxSelectedFrames).fill(null), |
|
perspectivizingFrames: Array(maxSelectedFrames).fill(Array(2).fill(null)), |
|
|
|
frameToAdd: "", |
|
showInvalidFrameWarning: false, |
|
|
|
|
|
keywordAreaShown: false, |
|
showInvalidKeywordWarning: false, |
|
keywordToAdd: "", |
|
selectedKeywords: [], |
|
keywordSuggestedFrames: [], |
|
|
|
|
|
textToAnalyze: "", |
|
analyzedSentences: [], |
|
analyzingTextBusy: false, |
|
analysisLanguage: "it" |
|
} |
|
}, |
|
|
|
mounted() { |
|
}, |
|
|
|
|
|
methods: { |
|
|
|
|
|
startInteractiveMode() { |
|
this.interactiveModeEnabled = true; |
|
console.log(this.$refs); |
|
setTimeout(() => { |
|
this.$refs.interactiveModeHeading.scrollIntoView({ behavior: "smooth" }); |
|
}, 100) |
|
}, |
|
|
|
|
|
|
|
showEventExample() { |
|
|
|
let numExamples = 1; |
|
let randNum = Math.floor(Math.random() * numExamples); |
|
|
|
const names = [ |
|
"Traffic crash", |
|
]; |
|
const descriptions = [ |
|
"An accident happens in which a driver hits a pedestrian with their car. Typically, this kind of event is framed with a strong focus on the victim and their actions, and less focus on the driver" |
|
] |
|
const textExamples = [ |
|
"Typical newspaper headline (from https://www.irishexaminer.com/news/arid-40791657.html): Cyclist, 70s, seriously injured following collision in Dublin.\nAlternative way of framing: Driver hits pedestrian with his car, sending the 70-year old man to hospital with heavy injuries." |
|
]; |
|
|
|
this.selectedExampleId = randNum; |
|
this.eventTypeName = names[randNum]; |
|
this.eventTypeDescr = descriptions[randNum]; |
|
this.eventTypeTextExample = textExamples[randNum]; |
|
}, |
|
|
|
|
|
showFrameExample() { |
|
let selectedExample = this.selectedExampleId; |
|
if (selectedExample === -1) { |
|
selectedExample = Math.floor(Math.random() * 3); |
|
this.selectedExample = selectedExample; |
|
} |
|
|
|
this.primaryFrames = this.primaryFrames.map(_ => null); |
|
switch (selectedExample) { |
|
case 0: |
|
this.switchFrame(0, "Impact"); |
|
setTimeout(() => {this.switchFrame(1, "Experience_bodily_harm")}, 50); |
|
setTimeout(() => {this.switchFrame(2, "Catastrophe")}, 100); |
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
}, |
|
|
|
addFrame(frmName) { |
|
this.frameToAdd = ""; |
|
|
|
let numSelectedFrames = this.primaryFrames.filter((fr) => fr !== null).length; |
|
if (numSelectedFrames >= maxSelectedFrames) { |
|
return; |
|
} |
|
|
|
let newFrameIdx = numSelectedFrames; |
|
this.switchFrame(newFrameIdx, frmName); |
|
|
|
}, |
|
|
|
switchFrame(targetIdx, frmName) { |
|
getFrameInfo(frmName, |
|
|
|
(data) => { |
|
this.showInvalidFrameWarning = false; |
|
console.log(data); |
|
|
|
this.primaryFrames[targetIdx] = { |
|
"name": frmName, |
|
"description": data["description"], |
|
"altPerspectives": [], |
|
"typicalExemplar": data["typicalExemplar"] |
|
}; |
|
|
|
data["altPerspectives"].forEach((persp) => { |
|
getFrameInfo(persp["frame"], |
|
|
|
(perspData) => { |
|
this.primaryFrames[targetIdx].altPerspectives.push({ |
|
"name": persp["frame"], |
|
"type": persp["type"][0].toUpperCase() + persp["type"].substring(1), |
|
"description": perspData["description"], |
|
"typicalExemplar": perspData["typicalExemplar"] |
|
}); |
|
|
|
}); |
|
}); |
|
}, |
|
|
|
() => { |
|
this.showInvalidFrameWarning = true; |
|
}); |
|
}, |
|
|
|
removeFrame(frmName) { |
|
this.primaryFrames = this.primaryFrames.filter((frm) => frm === null || frm.name !== frmName); |
|
for (let i = this.primaryFrames.length; i < maxSelectedFrames; i++) { |
|
this.primaryFrames.push(null); |
|
} |
|
}, |
|
|
|
|
|
|
|
showKeywords() { |
|
this.keywordAreaShown = true; |
|
setTimeout(() => { |
|
this.$refs.keywordHeading.scrollIntoView({ behavior: "smooth" }); |
|
}, 100) |
|
}, |
|
|
|
addKeyword() { |
|
this.selectedKeywords.push({ "text": this.keywordToAdd }); |
|
this.keywordToAdd = ""; |
|
this.updateKeywordSuggestedFrames(); |
|
}, |
|
|
|
updateKeywordSuggestedFrames() { |
|
this.keywordSuggestedFrames = []; |
|
$.get( |
|
"similar_frames", |
|
{ "words_in": this.selectedKeywords.map((kwd) => kwd.text).join("+") } |
|
).done((data) => { |
|
if (data.result === "FAIL") { |
|
this.showInvalidKeywordWarning = true; |
|
} else { |
|
this.showInvalidKeywordWarning = false; |
|
data["frames"].slice(0, 5).forEach((frm) => { |
|
getFrameInfo(frm, (frameInfo) => { |
|
console.log(frameInfo); |
|
this.keywordSuggestedFrames.push({ |
|
text: frm, |
|
description: frameInfo["description"], |
|
exemplars: frameInfo["exemplars"], |
|
altPerspectives: frameInfo["altPerspectives"], |
|
}) |
|
}); |
|
}); |
|
} |
|
}); |
|
}, |
|
|
|
removeKeyword(event) { |
|
this.selectedKeywords = this.selectedKeywords.filter((kwd) => kwd.text !== event.target.attributes["title"].value); |
|
this.updateKeywordSuggestedFrames(); |
|
}, |
|
|
|
|
|
analyzeText(text, language) { |
|
this.analyzedSentences = []; |
|
this.analyzingTextBusy = true; |
|
|
|
$.get("sociofillmore", { "text": text, "language": language }, |
|
|
|
(data) => { |
|
data.forEach((sentenceData) => { |
|
console.log(sentenceData); |
|
let textParts = []; |
|
sentenceData["sentence"].forEach((token, tokenIdx) => { |
|
let targets = sentenceData["fn_structures"].filter(struct => struct["target"]["tokens_idx"].includes(tokenIdx)); |
|
if (targets.length === 0) { |
|
textParts.push({ "text": token, "type": "nonTarget" }); |
|
} else { |
|
|
|
|
|
let target = targets[0]; |
|
let perspectiveType = "Main"; |
|
let perspectiveOn = target["frame"]; |
|
let subType = "otherTarget"; |
|
if (this.primaryFrames.filter(pf => pf !== null).map(pf => pf.name).includes(target["frame"])) { |
|
subType = "primaryTarget"; |
|
} else if (this.primaryFrames.filter(pf => pf !== null).flatMap(pf => pf.altPerspectives.map(ap => ap.name)).includes(target["frame"])) { |
|
let primary = this.primaryFrames.filter(pf => pf !== null && pf.altPerspectives.map(ap => ap.name).includes(target["frame"]))[0]; |
|
let alternate = primary.altPerspectives.filter(ap => ap.name === target["frame"])[0]; |
|
subType = "altTarget"; |
|
perspectiveType = alternate.type; |
|
perspectiveOn = primary.name; |
|
} |
|
|
|
|
|
let syntax = sentenceData["syntax"][tokenIdx][0]; |
|
let grammarInfo = getGrammarInfo(syntax); |
|
|
|
|
|
let roles = []; |
|
target["roles"].forEach(role => { |
|
let roleName = role[0]; |
|
let roleSpanIdx = role[1]["tokens_idx"]; |
|
let roleSpanTxt = role[1]["tokens_str"].join(" "); |
|
roles.push({ |
|
"name": roleName, |
|
"spanIdx": roleSpanIdx, |
|
"spanTxt": roleSpanTxt, |
|
"spanTxtClipped": roleSpanTxt.length > 50 ? roleSpanTxt.substring(0, 50) + "...": roleSpanTxt |
|
}) |
|
}); |
|
|
|
console.log(roles); |
|
textParts.push({ |
|
"text": token, |
|
"type": "target", |
|
"subType": subType, |
|
"frame": target["frame"], |
|
"perspectiveType": perspectiveType, |
|
"perspectiveOn": perspectiveOn, |
|
"grammarInfo": grammarInfo, |
|
"roles": roles |
|
}); |
|
} |
|
}); |
|
|
|
this.analyzedSentences.push({ |
|
"text": sentenceData["sentence"].join(" "), |
|
"textParts": textParts, |
|
"relevantTargets": textParts.filter(tp => (tp.type === "target" && tp.subType !== "otherTarget")) |
|
}); |
|
console.log(this.analyzedSentences); |
|
this.analyzingTextBusy = false; |
|
}); |
|
}); |
|
}, |
|
|
|
showTextExample() { |
|
this.analysisLanguage = "en"; |
|
this.textToAnalyze = this.eventTypeTextExample; |
|
} |
|
|
|
|
|
}, |
|
|
|
delimiters: ['@{', '}'] |
|
} |
|
|
|
|
|
function getFrameInfo(frm, successCallback, failureCallback) { |
|
|
|
$.get("frame_info", { "frame": frm }).done((data) => { |
|
|
|
if (data["result"] !== "OK") { |
|
failureCallback(); |
|
return; |
|
} |
|
|
|
let descriptionClipped = clipFrameDescription(data["frameDefinition"]); |
|
let url = "https://framenet2.icsi.berkeley.edu/fnReports/data/frameIndex.xml?frame=" + frm; |
|
descriptionClipped += ' <a href="' + url + '">(read more)</a>'; |
|
|
|
let sampledExemplars = sampleExemplars(data["exemplars"]); |
|
let exemplarsClipped = clipExemplars(sampledExemplars); |
|
|
|
let frameInfo = { |
|
"description": descriptionClipped, |
|
"exemplars": exemplarsClipped, |
|
"altPerspectives": data["altPerspectives"], |
|
"typicalExemplar": data["typicalExemplar"] |
|
}; |
|
successCallback(frameInfo); |
|
}) |
|
} |
|
|
|
|
|
function clipFrameDescription(descriptionParts) { |
|
console.log(descriptionParts); |
|
let descriptionClipped = ""; |
|
for (let i = 0; i < descriptionParts.length; i++) { |
|
let remainingLength = 300 - descriptionClipped.length; |
|
if (remainingLength >= 0) { |
|
let newPart = descriptionParts[i]; |
|
if (newPart.length > remainingLength) { |
|
descriptionClipped += newPart.substring(0, remainingLength) + "..."; |
|
} else { |
|
descriptionClipped += newPart; |
|
} |
|
} |
|
} |
|
return descriptionClipped; |
|
} |
|
|
|
function sampleExemplars(exemplars) { |
|
let sampledExemplars = []; |
|
let sampleSize = Math.min(10, exemplars.length); |
|
|
|
for (let i = 0; i < sampleSize; i++) { |
|
let filteredExemplars = exemplars.filter(((ex) => !sampledExemplars.includes(ex))); |
|
let randIdx = Math.floor(Math.random() * filteredExemplars.length); |
|
sampledExemplars.push(filteredExemplars[randIdx]); |
|
} |
|
return sampledExemplars; |
|
} |
|
|
|
function clipExemplars(exemplars) { |
|
|
|
return exemplars.map((ex) => { |
|
|
|
|
|
const maxContextChars = 35; |
|
|
|
let tgtStartIdx = ex["target_idx"][0]; |
|
let fragmentStartIdx = Math.max(0, tgtStartIdx - maxContextChars); |
|
let tgtEndIdx = ex["target_idx"][1]; |
|
let fragmentEndIdx = Math.min(ex["text"].length, tgtEndIdx + maxContextChars); |
|
|
|
|
|
let textWithCapitalizedTarget = |
|
ex["text"].substring(0, tgtStartIdx) + |
|
ex["text"].substring(tgtStartIdx, tgtEndIdx).toUpperCase() + |
|
ex["text"].substring(tgtEndIdx, ex["text"].length); |
|
; |
|
|
|
let clippedText = textWithCapitalizedTarget.split(" ").filter((_, idx, arr) => { |
|
let prevChars = arr.slice(0, idx).join(" ").length; |
|
if (prevChars >= fragmentStartIdx && prevChars <= fragmentEndIdx) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
}).join(" "); |
|
|
|
return { "text": clippedText }; |
|
}) |
|
} |
|
|
|
function getGrammarInfo(syntax) { |
|
let grammarInfo = "other"; |
|
if (syntax["syn_category"].startsWith("v:")) { |
|
switch (syntax["syn_construction"]) { |
|
case "verbal:active": |
|
grammarInfo = "active"; |
|
break; |
|
case "verbal:passive": |
|
grammarInfo = "passive"; |
|
break; |
|
case "verbal:unaccusative": |
|
grammarInfo = "intransitive"; |
|
break; |
|
case "verbal:impersonal": |
|
grammarInfo = "impersonal"; |
|
break; |
|
case "verbal:reflexive": |
|
grammarInfo = "reflexive"; |
|
break; |
|
} |
|
} else if (syntax["syn_category"] == "n") { |
|
grammarInfo = "noun"; |
|
} else if (syntax["syn_category"] == "adj") { |
|
grammarInfo = "adjective"; |
|
} |
|
return grammarInfo; |
|
} |
|
|
|
|
|
|
|
Vue.createApp(SocioFillmoreDemo).mount("#sociofillmore-demo"); |
|
|