OO-PG0020
Plug-In: Import Copied Spreadsheet
A specialized command for importing tab-return-delimited spreadsheet data from the clipboard. TIP: For best results, start with a new blank empty outline.
Columns and rows are added as required, with the option for the outline column (topic) to be used for row headers.
A form is presented prior to input enabling the user to indicate column formatting (numeric) and to indicate whether the copied data contains row and/or column headers.
|
|
Return to: OmniOutliner Plug-In Collection
Column Title Alignment
/*{"type": "action","targets": ["omnioutliner"],"author": "Otto Automator","identifier": "com.omni-automation.oo.import-copied-spreadsheet","version": "1.1","description": "A specialized command for importing tab-return-delimited spreadsheet data from the clipboard.","label": "Import Copied Spreadsheet","shortLabel": "Import Spreadsheet","paletteLabel": "Import Spreadsheet","image": "square.and.arrow.down.fill"}*/(() => {const action = new PlugIn.Action(async function(selection, sender){try {function createUtterance(textToSpeak){langCode = Speech.Voice.currentLanguageCodevoiceObj = Speech.Voice.withLanguage(langCode)utterance = new Speech.Utterance(textToSpeak)utterance.voice = voiceObjutterance.rate = Speech.Utterance.defaultSpeechRatereturn utterance}var synthesizer = new Speech.Synthesizer()try {document.name} catch(err){throw {name:"Missing Resource",message:"No outline document is open."}}// CHECK CLIPBOARD CONTENTvar clipText = Pasteboard.general.stringif(!clipText.includes("\t")){throw {name: "Missing Data",message: "There is no tab-delimited spreadsheet data on the clipboard."}}// CREATE FORMinputForm = new Form()formats = ["Text (abc)", "Number (123)", "Decimal (123.00)", "Percent (123%)", "Decimal Percent (123.12%)", "Decimal Separators (1,234.12)", "Currency"]formatMenu = new Form.Field.Option("formatType","Format",[0, 1, 2, 3, 4, 5, 6],formats,1)columnHeadersCheckbox = new Form.Field.Checkbox("firstRowIsColHeaders","1st data row is column headers",true)rowHeadersCheckbox = new Form.Field.Checkbox("firstColumnIsRowHeaders","1st data column is row headers",true)inputForm.addField(formatMenu)inputForm.addField(columnHeadersCheckbox)inputForm.addField(rowHeadersCheckbox)// PRESENT FORM WITH SPOKEN PROMPTformPrompt = "Format for added columns:"formButtom = "Continue"spokenPrompt = "Please indicate the format and parameters for the added columns."utterance = createUtterance(spokenPrompt)synthesizer.speakUtterance(utterance)formObject = await inputForm.show(formPrompt, formButtom)synthesizer.stopSpeaking(Speech.Boundary.Word)// RETRIEVE FORM RESULTSvar firstRowIsColHeaders = formObject.values["firstRowIsColHeaders"]var firstColumnIsRowHeaders = formObject.values["firstColumnIsRowHeaders"]var formatType = formObject.values['formatType']switch(formatType) {case 0:var columnType = Column.Type.Textvar fmtr = nullbreak;case 1:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.plainbreak;case 2:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.decimalbreak;case 3:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.percentbreak;case 4:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.percentWithDecimalbreak;case 5:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.thousandsAndDecimalbreak;case 6:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.currency()break;default:var columnType = Column.Type.Numbervar fmtr = Formatter.Decimal.plainbreak;}// CLEAN CLIPBOARD TEXT TO ARRAY OF ROW DATAdataRows = clipText.split("\n")var tableData = []dataRows.forEach(dataRow => {tableData.push(dataRow.split("\t"))})// ADD COLUMNS AS REQUIREDvar tree = document.outlinevar editor = document.editors[0]editor.autosizeTopicColumn = trueif(firstRowIsColHeaders){columnTitles = tableData[0]tableData.shift()}var numRows = tableData.lengthvar numCols = tableData[0].lengthvar columnRefs = []for (i = 0; i < numCols; i++) {if(firstColumnIsRowHeaders && firstRowIsColHeaders && i === 0){columnRefs.push(outlineColumn)outlineColumn.title = columnTitles[0]} else {newCol = tree.addColumn(columnType,editor.afterColumn(null),column => {if(firstRowIsColHeaders){column.title = columnTitles[i]}if(fmtr){column.formatter = fmtr}})columnRefs.push(newCol)}}// REMOVE GLOBAL COLUMN TITLE ALIGNMENTScolumnTitleStyle.set(Style.Attribute.ParagraphAlignment, null)// POPULATE THE OUTLINE WITH DATAtableData.forEach(dataRow => {tree.rootItem.addChild(null, item => {columnRefs.forEach((col, index) => {dataItem = dataRow[index]if(firstColumnIsRowHeaders && index === 0){item.setValueForColumn(dataItem, outlineColumn)} else {if(formatType !== 0){// REMOVE NON-NUMERIC CHARACTERS ($, €, £, etc.) EXCEPT "."dataItem = dataItem.replace(/[^\d\.]/g,"")dataItem = parseFloat(dataItem)}item.setValueForColumn(dataItem, col)}})})})utterance = createUtterance("Done!")synthesizer.speakUtterance(utterance)}catch(err){if(!causedByUserCancelling){utterance = createUtterance(err.message)synthesizer.speakUtterance(utterance)new Alert(err.name, err.message).show()}}});action.validate = function(selection, sender){return Pasteboard.general.hasStrings};return action;})();