- // ==UserScript==
- // @name Desmos csv import
- // @namespace http://tampermonkey.net/
- // @version 2024-11-12
- // @description Just a little helper userscipt to convert csv into a format that desmos likes
- // @author Evan Plaice
- // @author Pranshu Tanwar
- // @match https://www.desmos.com/calculator*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant unsafeWindow
- // @license MIT
- // ==/UserScript==
-
- // HOW TO USE (in browser console with desmos open)
- // define csv = `your csv as a string`
- // call to_add = desmoscsv.csv.toDesmosTable(csv)
- // state = Calc.getState()
- // state.expressions.list.push(to_add)
- // Calc.setState(state)
-
-
- /* eslint no-prototype-builtins: 0 */
-
-
- /**
- * jQuery-csv (jQuery Plugin)
- *
- * This document is licensed as free software under the terms of the
- * MIT License: http://www.opensource.org/licenses/mit-license.php
- *
- * Acknowledgements:
- * The original design and influence to implement this library as a jquery
- * plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
- * If you're looking to use native JSON.Stringify but want additional backwards
- * compatibility for browsers that don't support it, I highly recommend you
- * check it out.
- *
- * A special thanks goes out to rwk@acm.org for providing a lot of valuable
- * feedback to the project including the core for the new FSM
- * (Finite State Machine) parsers. If you're looking for a stable TSV parser
- * be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
-
- * For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
- * USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
- * library you are accepting responsibility if it breaks your code.
- *
- * Legal jargon aside, I will do my best to provide a useful and stable core
- * that can effectively be built on.
- *
- * Copyrighted 2012 by Evan Plaice.
- */
-
- RegExp.escape = function (s) {
- return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
- };
-
- (function () {
- 'use strict'
-
- let $
-
- $ = {}
- /**
- * jQuery.csv.defaults
- * Encapsulates the method paramater defaults for the CSV plugin module.
- */
-
- $.csv = {
- defaults: {
- separator: ',',
- delimiter: '"',
- headers: true
- },
-
- hooks: {
- castToScalar: function (value, state) {
- const hasDot = /\./
- if (isNaN(value)) {
- return value
- } else {
- if (hasDot.test(value)) {
- return parseFloat(value)
- } else {
- const integer = parseInt(value)
- if (isNaN(integer)) {
- return null
- } else {
- return integer
- }
- }
- }
- }
- },
-
- parsers: {
- parse: function (csv, options) {
- // cache settings
- const separator = options.separator
- const delimiter = options.delimiter
-
- // set initial state if it's missing
- if (!options.state.rowNum) {
- options.state.rowNum = 1
- }
- if (!options.state.colNum) {
- options.state.colNum = 1
- }
-
- // clear initial state
- const data = []
- let entry = []
- let state = 0
- let value = ''
- let exit = false
-
- function endOfEntry () {
- // reset the state
- state = 0
- value = ''
-
- // if 'start' hasn't been met, don't output
- if (options.start && options.state.rowNum < options.start) {
- // update global state
- entry = []
- options.state.rowNum++
- options.state.colNum = 1
- return
- }
-
- if (options.onParseEntry === undefined) {
- // onParseEntry hook not set
- data.push(entry)
- } else {
- const hookVal = options.onParseEntry(entry, options.state) // onParseEntry Hook
- // false skips the row, configurable through a hook
- if (hookVal !== false) {
- data.push(hookVal)
- }
- }
- // console.log('entry:' + entry);
-
- // cleanup
- entry = []
-
- // if 'end' is met, stop parsing
- if (options.end && options.state.rowNum >= options.end) {
- exit = true
- }
-
- // update global state
- options.state.rowNum++
- options.state.colNum = 1
- }
-
- function endOfValue () {
- if (options.onParseValue === undefined) {
- // onParseValue hook not set
- entry.push(value)
- } else if (options.headers && options.state.rowNum === 1) {
- // don't onParseValue object headers
- entry.push(value)
- } else {
- const hook = options.onParseValue(value, options.state) // onParseValue Hook
- // false skips the row, configurable through a hook
- if (hook !== false) {
- entry.push(hook)
- }
- }
- // console.log('value:' + value);
- // reset the state
- value = ''
- state = 0
- // update global state
- options.state.colNum++
- }
-
- // escape regex-specific control chars
- const escSeparator = RegExp.escape(separator)
- const escDelimiter = RegExp.escape(delimiter)
-
- // compile the regEx str using the custom delimiter/separator
- let match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/
- let matchSrc = match.source
- matchSrc = matchSrc.replace(/S/g, escSeparator)
- matchSrc = matchSrc.replace(/D/g, escDelimiter)
- match = new RegExp(matchSrc, 'gm')
-
- // put on your fancy pants...
- // process control chars individually, use look-ahead on non-control chars
- csv.replace(match, function (m0) {
- if (exit) {
- return
- }
- switch (state) {
- // the start of a value
- case 0:
- // null last value
- if (m0 === separator) {
- value += ''
- endOfValue()
- break
- }
- // opening delimiter
- if (m0 === delimiter) {
- state = 1
- break
- }
- // null last value
- if (/^(\r\n|\n|\r)$/.test(m0)) {
- endOfValue()
- endOfEntry()
- break
- }
- // un-delimited value
- value += m0
- state = 3
- break
-
- // delimited input
- case 1:
- // second delimiter? check further
- if (m0 === delimiter) {
- state = 2
- break
- }
- // delimited data
- value += m0
- state = 1
- break
-
- // delimiter found in delimited input
- case 2:
- // escaped delimiter?
- if (m0 === delimiter) {
- value += m0
- state = 1
- break
- }
- // null value
- if (m0 === separator) {
- endOfValue()
- break
- }
- // end of entry
- if (/^(\r\n|\n|\r)$/.test(m0)) {
- endOfValue()
- endOfEntry()
- break
- }
- // broken paser?
- throw Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
-
- // un-delimited input
- case 3:
- // null last value
- if (m0 === separator) {
- endOfValue()
- break
- }
- // end of entry
- if (/^(\r\n|\n|\r)$/.test(m0)) {
- endOfValue()
- endOfEntry()
- break
- }
- if (m0 === delimiter) {
- // non-compliant data
- throw Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- }
- // broken parser?
- throw Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- default:
- // shenanigans
- throw Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- }
- // console.log('val:' + m0 + ' state:' + state);
- })
-
- // submit the last entry
- // ignore null last line
- if (entry.length !== 0) {
- endOfValue()
- endOfEntry()
- }
-
- return data
- },
-
- // a csv-specific line splitter
- splitLines: function (csv, options) {
- if (!csv) {
- return undefined
- }
-
- options = options || {}
-
- // cache settings
- const separator = options.separator || $.csv.defaults.separator
- const delimiter = options.delimiter || $.csv.defaults.delimiter
-
- // set initial state if it's missing
- options.state = options.state || {}
- if (!options.state.rowNum) {
- options.state.rowNum = 1
- }
-
- // clear initial state
- const entries = []
- let state = 0
- let entry = ''
- let exit = false
-
- function endOfLine () {
- // reset the state
- state = 0
-
- // if 'start' hasn't been met, don't output
- if (options.start && options.state.rowNum < options.start) {
- // update global state
- entry = ''
- options.state.rowNum++
- return
- }
-
- if (options.onParseEntry === undefined) {
- // onParseEntry hook not set
- entries.push(entry)
- } else {
- const hookVal = options.onParseEntry(entry, options.state) // onParseEntry Hook
- // false skips the row, configurable through a hook
- if (hookVal !== false) {
- entries.push(hookVal)
- }
- }
-
- // cleanup
- entry = ''
-
- // if 'end' is met, stop parsing
- if (options.end && options.state.rowNum >= options.end) {
- exit = true
- }
-
- // update global state
- options.state.rowNum++
- }
-
- // escape regex-specific control chars
- const escSeparator = RegExp.escape(separator)
- const escDelimiter = RegExp.escape(delimiter)
-
- // compile the regEx str using the custom delimiter/separator
- let match = /(D|S|\n|\r|[^DS\r\n]+)/
- let matchSrc = match.source
- matchSrc = matchSrc.replace(/S/g, escSeparator)
- matchSrc = matchSrc.replace(/D/g, escDelimiter)
- match = new RegExp(matchSrc, 'gm')
-
- // put on your fancy pants...
- // process control chars individually, use look-ahead on non-control chars
- csv.replace(match, function (m0) {
- if (exit) {
- return
- }
- switch (state) {
- // the start of a value/entry
- case 0:
- // null value
- if (m0 === separator) {
- entry += m0
- state = 0
- break
- }
- // opening delimiter
- if (m0 === delimiter) {
- entry += m0
- state = 1
- break
- }
- // end of line
- if (m0 === '\n') {
- endOfLine()
- break
- }
- // phantom carriage return
- if (/^\r$/.test(m0)) {
- break
- }
- // un-delimit value
- entry += m0
- state = 3
- break
-
- // delimited input
- case 1:
- // second delimiter? check further
- if (m0 === delimiter) {
- entry += m0
- state = 2
- break
- }
- // delimited data
- entry += m0
- state = 1
- break
-
- // delimiter found in delimited input
- case 2: {
- // escaped delimiter?
- const prevChar = entry.substr(entry.length - 1)
- if (m0 === delimiter && prevChar === delimiter) {
- entry += m0
- state = 1
- break
- }
- // end of value
- if (m0 === separator) {
- entry += m0
- state = 0
- break
- }
- // end of line
- if (m0 === '\n') {
- endOfLine()
- break
- }
- // phantom carriage return
- if (m0 === '\r') {
- break
- }
- // broken paser?
- throw Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']')
- }
- // un-delimited input
- case 3:
- // null value
- if (m0 === separator) {
- entry += m0
- state = 0
- break
- }
- // end of line
- if (m0 === '\n') {
- endOfLine()
- break
- }
- // phantom carriage return
- if (m0 === '\r') {
- break
- }
- // non-compliant data
- if (m0 === delimiter) {
- throw Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']')
- }
- // broken parser?
- throw Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']')
- default:
- // shenanigans
- throw Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']')
- }
- // console.log('val:' + m0 + ' state:' + state);
- })
-
- // submit the last entry
- // ignore null last line
- if (entry !== '') {
- endOfLine()
- }
-
- return entries
- },
-
- // a csv entry parser
- parseEntry: function (csv, options) {
- // cache settings
- const separator = options.separator
- const delimiter = options.delimiter
-
- // set initial state if it's missing
- if (!options.state.rowNum) {
- options.state.rowNum = 1
- }
- if (!options.state.colNum) {
- options.state.colNum = 1
- }
-
- // clear initial state
- const entry = []
- let state = 0
- let value = ''
-
- function endOfValue () {
- if (options.onParseValue === undefined) {
- // onParseValue hook not set
- entry.push(value)
- } else {
- const hook = options.onParseValue(value, options.state) // onParseValue Hook
- // false skips the value, configurable through a hook
- if (hook !== false) {
- entry.push(hook)
- }
- }
- // reset the state
- value = ''
- state = 0
- // update global state
- options.state.colNum++
- }
-
- // checked for a cached regEx first
- if (!options.match) {
- // escape regex-specific control chars
- const escSeparator = RegExp.escape(separator)
- const escDelimiter = RegExp.escape(delimiter)
-
- // compile the regEx str using the custom delimiter/separator
- const match = /(D|S|\n|\r|[^DS\r\n]+)/
- let matchSrc = match.source
- matchSrc = matchSrc.replace(/S/g, escSeparator)
- matchSrc = matchSrc.replace(/D/g, escDelimiter)
- options.match = new RegExp(matchSrc, 'gm')
- }
-
- // put on your fancy pants...
- // process control chars individually, use look-ahead on non-control chars
- csv.replace(options.match, function (m0) {
- switch (state) {
- // the start of a value
- case 0:
- // null last value
- if (m0 === separator) {
- value += ''
- endOfValue()
- break
- }
- // opening delimiter
- if (m0 === delimiter) {
- state = 1
- break
- }
- // skip un-delimited new-lines
- if (m0 === '\n' || m0 === '\r') {
- break
- }
- // un-delimited value
- value += m0
- state = 3
- break
-
- // delimited input
- case 1:
- // second delimiter? check further
- if (m0 === delimiter) {
- state = 2
- break
- }
- // delimited data
- value += m0
- state = 1
- break
-
- // delimiter found in delimited input
- case 2:
- // escaped delimiter?
- if (m0 === delimiter) {
- value += m0
- state = 1
- break
- }
- // null value
- if (m0 === separator) {
- endOfValue()
- break
- }
- // skip un-delimited new-lines
- if (m0 === '\n' || m0 === '\r') {
- break
- }
- // broken paser?
- throw Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
-
- // un-delimited input
- case 3:
- // null last value
- if (m0 === separator) {
- endOfValue()
- break
- }
- // skip un-delimited new-lines
- if (m0 === '\n' || m0 === '\r') {
- break
- }
- // non-compliant data
- if (m0 === delimiter) {
- throw Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- }
- // broken parser?
- throw Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- default:
- // shenanigans
- throw Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']')
- }
- // console.log('val:' + m0 + ' state:' + state);
- })
-
- // submit the last value
- endOfValue()
-
- return entry
- }
- },
-
- helpers: {
-
- /**
- * $.csv.helpers.collectPropertyNames(objectsArray)
- * Collects all unique property names from all passed objects.
- *
- * @param {Array} objects Objects to collect properties from.
- *
- * Returns an array of property names (array will be empty,
- * if objects have no own properties).
- */
- collectPropertyNames: function (objects) {
- let o = []
- let propName = []
- const props = []
- for (o in objects) {
- for (propName in objects[o]) {
- if ((objects[o].hasOwnProperty(propName)) &&
- (props.indexOf(propName) < 0) &&
- (typeof objects[o][propName] !== 'function')) {
- props.push(propName)
- }
- }
- }
- return props
- }
- },
-
- /**
- * $.csv.toArray(csv)
- * Converts a CSV entry string to a javascript array.
- *
- * @param {Array} csv The string containing the CSV data.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- *
- * This method deals with simple CSV strings only. It's useful if you only
- * need to parse a single entry. If you need to parse more than one line,
- * use $.csv2Array instead.
- */
- toArray: function (csv, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
- const state = (options.state !== undefined ? options.state : {})
-
- // setup
- options = {
- delimiter: config.delimiter,
- separator: config.separator,
- onParseEntry: options.onParseEntry,
- onParseValue: options.onParseValue,
- state
- }
-
- const entry = $.csv.parsers.parseEntry(csv, options)
-
- // push the value to a callback if one is defined
- if (!config.callback) {
- return entry
- } else {
- config.callback('', entry)
- }
- },
-
- /**
- * $.csv.toArrays(csv)
- * Converts a CSV string to a javascript array.
- *
- * @param {String} csv The string containing the raw CSV data.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- *
- * This method deals with multi-line CSV. The breakdown is simple. The first
- * dimension of the array represents the line (or entry/row) while the second
- * dimension contains the values (or values/columns).
- */
- toArrays: function (csv, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
-
- // setup
- let data = []
- options = {
- delimiter: config.delimiter,
- separator: config.separator,
- onPreParse: options.onPreParse,
- onParseEntry: options.onParseEntry,
- onParseValue: options.onParseValue,
- onPostParse: options.onPostParse,
- start: options.start,
- end: options.end,
- state: {
- rowNum: 1,
- colNum: 1
- }
- }
-
- // onPreParse hook
- if (options.onPreParse !== undefined) {
- csv = options.onPreParse(csv, options.state)
- }
-
- // parse the data
- data = $.csv.parsers.parse(csv, options)
-
- // onPostParse hook
- if (options.onPostParse !== undefined) {
- data = options.onPostParse(data, options.state)
- }
-
- // push the value to a callback if one is defined
- if (!config.callback) {
- return data
- } else {
- config.callback('', data)
- }
- },
-
- /**
- * $.csv.toObjects(csv)
- * Converts a CSV string to a javascript object.
- * @param {String} csv The string containing the raw CSV data.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- * @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
- *
- * This method deals with multi-line CSV strings. Where the headers line is
- * used as the key for each value per entry.
- */
- toObjects: function (csv, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
- config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers
- options.start = 'start' in options ? options.start : 1
-
- // account for headers
- if (config.headers) {
- options.start++
- }
- if (options.end && config.headers) {
- options.end++
- }
-
- // setup
- let lines = []
- let data = []
-
- options = {
- delimiter: config.delimiter,
- separator: config.separator,
- onPreParse: options.onPreParse,
- onParseEntry: options.onParseEntry,
- onParseValue: options.onParseValue,
- onPostParse: options.onPostParse,
- start: options.start,
- end: options.end,
- state: {
- rowNum: 1,
- colNum: 1
- },
- match: false,
- transform: options.transform
- }
-
- // fetch the headers
- const headerOptions = {
- delimiter: config.delimiter,
- separator: config.separator,
- start: 1,
- end: 1,
- state: {
- rowNum: 1,
- colNum: 1
- },
- headers: true
- }
-
- // onPreParse hook
- if (options.onPreParse !== undefined) {
- csv = options.onPreParse(csv, options.state)
- }
-
- // parse the csv
- const headerLine = $.csv.parsers.splitLines(csv, headerOptions)
- const headers = $.csv.toArray(headerLine[0], headerOptions)
-
- // fetch the data
- lines = $.csv.parsers.splitLines(csv, options)
-
- // reset the state for re-use
- options.state.colNum = 1
- if (headers) {
- options.state.rowNum = 2
- } else {
- options.state.rowNum = 1
- }
-
- // convert data to objects
- for (let i = 0, len = lines.length; i < len; i++) {
- const entry = $.csv.toArray(lines[i], options)
- const object = {}
- for (let j = 0; j < headers.length; j++) {
- object[headers[j]] = entry[j]
- }
- if (options.transform !== undefined) {
- data.push(options.transform.call(undefined, object))
- } else {
- data.push(object)
- }
-
- // update row state
- options.state.rowNum++
- }
-
- // onPostParse hook
- if (options.onPostParse !== undefined) {
- data = options.onPostParse(data, options.state)
- }
-
- // push the value to a callback if one is defined
- if (!config.callback) {
- return data
- } else {
- config.callback('', data)
- }
- },
-
- /**
- below is custom added code
- * $.csv.toDesmosTable(csv)
- * Converts a CSV string to a javascript array.
- *
- * @param {String} csv The string containing the raw CSV data.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- *
- * This method deals with multi-line CSV. The breakdown is simple. The first
- * dimension of the array represents the line (or entry/row) while the second
- * dimension contains the values (or values/columns).
- */
- toDesmosTable: function (csv, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
-
- // setup
- let data = []
- options = {
- delimiter: config.delimiter,
- separator: config.separator,
- onPreParse: options.onPreParse,
- onParseEntry: options.onParseEntry,
- onParseValue: options.onParseValue,
- onPostParse: options.onPostParse,
- start: options.start,
- end: options.end,
- state: {
- rowNum: 1,
- colNum: 1
- }
- }
-
- let result = []
- let arr = $.csv.toArrays(csv, options)
- for (let i = 0; i < arr.length; i++) {
- let inner = arr[i]
- if (i == 0) {
- result = new Array(inner.length)
- for (let v = 0; v < inner.length; v++) {
- result[v] = {color: "#eeeeee", values: [inner[v]]}
- }
- }
- else {
- for (let v = 0; v < inner.length; v++) {
- result[v].values.push(inner[v])
- }
- }
- }
- let ret = new Object();
- ret.type = "table"
- ret.columns = result
- return ret
- }
- ,
- /**
- above is custom added code
- */
-
- /**
- * $.csv.fromArrays(arrays)
- * Converts a javascript array to a CSV String.
- *
- * @param {Array} arrays An array containing an array of CSV entries.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- *
- * This method generates a CSV file from an array of arrays (representing entries).
- */
- fromArrays: function (arrays, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
-
- let output = ''
-
- for (let i = 0; i < arrays.length; i++) {
- const line = arrays[i]
- const lineValues = []
- for (let j = 0; j < line.length; j++) {
- let strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString()
- if (strValue.indexOf(config.delimiter) > -1) {
- strValue = strValue.replace(new RegExp(config.delimiter, 'g'), config.delimiter + config.delimiter)
- }
-
- let escMatcher = '\n|\r|S|D'
- escMatcher = escMatcher.replace('S', config.separator)
- escMatcher = escMatcher.replace('D', config.delimiter)
-
- if (strValue.search(escMatcher) > -1) {
- strValue = config.delimiter + strValue + config.delimiter
- }
- lineValues.push(strValue)
- }
- output += lineValues.join(config.separator) + '\n'
- }
-
- // push the value to a callback if one is defined
- if (!config.callback) {
- return output
- } else {
- config.callback('', output)
- }
- },
-
- /**
- * $.csv.fromObjects(objects)
- * Converts a javascript dictionary to a CSV string.
- *
- * @param {Object} objects An array of objects containing the data.
- * @param {Object} [options] An object containing user-defined options.
- * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
- * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
- * @param {Character} [sortOrder] Sort order of columns (named after
- * object properties). Use 'alpha' for alphabetic. Default is 'declare',
- * which means, that properties will _probably_ appear in order they were
- * declared for the object. But without any guarantee.
- * @param {Character or Array} [manualOrder] Manually order columns. May be
- * a strin in a same csv format as an output or an array of header names
- * (array items won't be parsed). All the properties, not present in
- * `manualOrder` will be appended to the end in accordance with `sortOrder`
- * option. So the `manualOrder` always takes preference, if present.
- *
- * This method generates a CSV file from an array of objects (name:value pairs).
- * It starts by detecting the headers and adding them as the first line of
- * the CSV file, followed by a structured dump of the data.
- */
- fromObjects: function (objects, options, callback) {
- // if callback was passed to options swap callback with options
- if (options !== undefined && typeof (options) === 'function') {
- if (callback !== undefined) {
- return console.error('You cannot 3 arguments with the 2nd argument being a function')
- }
- callback = options
- options = {}
- }
-
- options = (options !== undefined ? options : {})
- const config = {}
- config.callback = ((callback !== undefined && typeof (callback) === 'function') ? callback : false)
- config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator
- config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter
- config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers
- config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare'
- config.manualOrder = 'manualOrder' in options ? options.manualOrder : []
- config.transform = options.transform
-
- if (typeof config.manualOrder === 'string') {
- config.manualOrder = $.csv.toArray(config.manualOrder, config)
- }
-
- if (config.transform !== undefined) {
- const origObjects = objects
- objects = []
-
- for (let i = 0; i < origObjects.length; i++) {
- objects.push(config.transform.call(undefined, origObjects[i]))
- }
- }
-
- let props = $.csv.helpers.collectPropertyNames(objects)
-
- if (config.sortOrder === 'alpha') {
- props.sort()
- }
-
- if (config.manualOrder.length > 0) {
- const propsManual = [].concat(config.manualOrder)
-
- for (let p = 0; p < props.length; p++) {
- if (propsManual.indexOf(props[p]) < 0) {
- propsManual.push(props[p])
- }
- }
- props = propsManual
- }
-
- let line
- const output = []
- let propName
- if (config.headers) {
- output.push(props)
- }
-
- for (let o = 0; o < objects.length; o++) {
- line = []
- for (let p = 0; p < props.length; p++) {
- propName = props[p]
- if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
- line.push(objects[o][propName])
- } else {
- line.push('')
- }
- }
- output.push(line)
- }
-
- // push the value to a callback if one is defined
- return $.csv.fromArrays(output, options, config.callback)
- }
- }
-
- // Maintenance code to maintain backward-compatibility
- // Will be removed in release 1.0
- $.csvEntry2Array = $.csv.toArray
- $.csv2Array = $.csv.toArrays
- $.csv2Dictionary = $.csv.toObjects
-
- this.$ = $
- }).call(this)
-
- if (unsafeWindow.desmoscsv == undefined) {
- unsafeWindow.desmoscsv = this.$
- }