/* CONSTANTS */ const maxSelectedFrames = 3; /* VUE APP */ const SocioFillmoreDemo = { data() { return { interactiveModeEnabled: false, showIntroductionBox: true, showExplanationText: true, // examples selectedExampleId: -1, // event types eventTypeName: "", eventTypeDescr: "", eventTypeTextExample: "", // frames primaryFrames: Array(maxSelectedFrames).fill(null), perspectivizingFrames: Array(maxSelectedFrames).fill(Array(2).fill(null)), frameToAdd: "", showInvalidFrameWarning: false, // keywords (for frame selection) keywordAreaShown: false, showInvalidKeywordWarning: false, keywordToAdd: "", selectedKeywords: [], keywordSuggestedFrames: [], // text analysis textToAnalyze: "", analyzedSentences: [], analyzingTextBusy: false, analysisLanguage: "it" } }, mounted() { }, // methods listed in order as shown in UI methods: { // mode 1: interactive mode startInteractiveMode() { this.interactiveModeEnabled = true; console.log(this.$refs); setTimeout(() => { this.$refs.interactiveModeHeading.scrollIntoView({ behavior: "smooth" }); }, 100) }, // 1A: define event type 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]; }, // 1B: defining frames 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: // measles this.switchFrame(0, "Impact"); setTimeout(() => {this.switchFrame(1, "Experience_bodily_harm")}, 50); setTimeout(() => {this.switchFrame(2, "Catastrophe")}, 100); break; // case 1: // this.switchFrame(0, "Win_prize"); // this.switchFrame(1, "Self_motion"); // this.switchFrame(2, "Motion_directional"); // break; // case 2: // this.switchFrame(0, "Win_prize"); // this.switchFrame(1, "Self_motion"); // this.switchFrame(2, "Motion_directional"); // 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, // success (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"], // success (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.analyzeText(this.textToAnalyze); }); }); }, // failure () => { 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); } }, // 1C: keyword-assisted frame selection 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(); }, // 1D: on-the-fly text analysis analyzeText(text, language) { this.analyzedSentences = []; this.analyzingTextBusy = true; $.get("sociofillmore", { "text": text, "language": language }, // success (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 { // frame & perspective 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; } // grammar info let syntax = sentenceData["syntax"][tokenIdx][0]; let grammarInfo = getGrammarInfo(syntax); // FEs 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: ['@{', '}'] } /* HELPER FUNCTIONS */ 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 += ' (read more)'; 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) { // clip exemplars to show the target + some context before and after return exemplars.map((ex) => { // target +/- N characters 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); // adjust for word boundaries 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; } /* MAIN LOOP */ Vue.createApp(SocioFillmoreDemo).mount("#sociofillmore-demo");