/js/libs/jquery.csv.js

https://github.com/mcoulthurst/Alpha · JavaScript · 848 lines · 721 code · 30 blank · 97 comment · 27 complexity · 05bf9cc74a6ad4ef4096268db055f8f5 MD5 · raw file

  1. /**
  2. * jQuery-csv (jQuery Plugin)
  3. * version: 0.71 (2012-11-19)
  4. *
  5. * This document is licensed as free software under the terms of the
  6. * MIT License: http://www.opensource.org/licenses/mit-license.php
  7. *
  8. * Acknowledgements:
  9. * The original design and influence to implement this library as a jquery
  10. * plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
  11. * If you're looking to use native JSON.Stringify but want additional backwards
  12. * compatibility for browsers that don't support it, I highly recommend you
  13. * check it out.
  14. *
  15. * A special thanks goes out to rwk@acm.org for providing a lot of valuable
  16. * feedback to the project including the core for the new FSM
  17. * (Finite State Machine) parsers. If you're looking for a stable TSV parser
  18. * be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
  19. * For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
  20. * USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
  21. * library you are accepting responsibility if it breaks your code.
  22. *
  23. * Legal jargon aside, I will do my best to provide a useful and stable core
  24. * that can effectively be built on.
  25. *
  26. * Copyrighted 2012 by Evan Plaice.
  27. */
  28. RegExp.escape= function(s) {
  29. return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  30. };
  31. (function( $ ) {
  32. 'use strict'
  33. /**
  34. * jQuery.csv.defaults
  35. * Encapsulates the method paramater defaults for the CSV plugin module.
  36. */
  37. $.csv = {
  38. defaults: {
  39. separator:',',
  40. delimiter:'"',
  41. headers:true
  42. },
  43. hooks: {
  44. castToScalar: function(value, state) {
  45. var hasDot = /\./;
  46. if (isNaN(value)) {
  47. return value;
  48. } else {
  49. if (hasDot.test(value)) {
  50. return parseFloat(value);
  51. } else {
  52. var integer = parseInt(value);
  53. if(isNaN(integer)) {
  54. return null;
  55. } else {
  56. return integer;
  57. }
  58. }
  59. }
  60. }
  61. },
  62. parsers: {
  63. parse: function(csv, options) {
  64. // cache settings
  65. var separator = options.separator;
  66. var delimiter = options.delimiter;
  67. // set initial state if it's missing
  68. if(!options.state.rowNum) {
  69. options.state.rowNum = 1;
  70. }
  71. if(!options.state.colNum) {
  72. options.state.colNum = 1;
  73. }
  74. // clear initial state
  75. var data = [];
  76. var entry = [];
  77. var state = 0;
  78. var value = ''
  79. var exit = false;
  80. function endOfEntry() {
  81. // reset the state
  82. state = 0;
  83. value = '';
  84. // if 'start' hasn't been met, don't output
  85. if(options.start && options.state.rowNum < options.start) {
  86. // update global state
  87. entry = [];
  88. options.state.rowNum++;
  89. options.state.colNum = 1;
  90. return;
  91. }
  92. if(options.onParseEntry === undefined) {
  93. // onParseEntry hook not set
  94. data.push(entry);
  95. } else {
  96. var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
  97. // false skips the row, configurable through a hook
  98. if(hookVal !== false) {
  99. data.push(hookVal);
  100. }
  101. }
  102. //console.log('entry:' + entry);
  103. // cleanup
  104. entry = [];
  105. // if 'end' is met, stop parsing
  106. if(options.end && options.state.rowNum >= options.end) {
  107. exit = true;
  108. }
  109. // update global state
  110. options.state.rowNum++;
  111. options.state.colNum = 1;
  112. }
  113. function endOfValue() {
  114. if(options.onParseValue === undefined) {
  115. // onParseValue hook not set
  116. entry.push(value);
  117. } else {
  118. var hook = options.onParseValue(value, options.state); // onParseValue Hook
  119. // false skips the row, configurable through a hook
  120. if(hook !== false) {
  121. entry.push(hook);
  122. }
  123. }
  124. //console.log('value:' + value);
  125. // reset the state
  126. value = '';
  127. state = 0;
  128. // update global state
  129. options.state.colNum++;
  130. }
  131. // escape regex-specific control chars
  132. var escSeparator = RegExp.escape(separator);
  133. var escDelimiter = RegExp.escape(delimiter);
  134. // compile the regEx str using the custom delimiter/separator
  135. var match = /(D|S|\n|\r|[^DS\r\n]+)/;
  136. var matchSrc = match.source;
  137. matchSrc = matchSrc.replace(/S/g, escSeparator);
  138. matchSrc = matchSrc.replace(/D/g, escDelimiter);
  139. match = RegExp(matchSrc, 'gm');
  140. // put on your fancy pants...
  141. // process control chars individually, use look-ahead on non-control chars
  142. csv.replace(match, function (m0) {
  143. if(exit) {
  144. return;
  145. }
  146. switch (state) {
  147. // the start of a value
  148. case 0:
  149. // null last value
  150. if (m0 === separator) {
  151. value += '';
  152. endOfValue();
  153. break;
  154. }
  155. // opening delimiter
  156. if (m0 === delimiter) {
  157. state = 1;
  158. break;
  159. }
  160. // null last value
  161. if (m0 === '\n') {
  162. endOfValue();
  163. endOfEntry();
  164. break;
  165. }
  166. // phantom carriage return
  167. if (/^\r$/.test(m0)) {
  168. break;
  169. }
  170. // un-delimited value
  171. value += m0;
  172. state = 3;
  173. break;
  174. // delimited input
  175. case 1:
  176. // second delimiter? check further
  177. if (m0 === delimiter) {
  178. state = 2;
  179. break;
  180. }
  181. // delimited data
  182. value += m0;
  183. state = 1;
  184. break;
  185. // delimiter found in delimited input
  186. case 2:
  187. // escaped delimiter?
  188. if (m0 === delimiter) {
  189. value += m0;
  190. state = 1;
  191. break;
  192. }
  193. // null value
  194. if (m0 === separator) {
  195. endOfValue();
  196. break;
  197. }
  198. // end of entry
  199. if (m0 === '\n') {
  200. endOfValue();
  201. endOfEntry();
  202. break;
  203. }
  204. // phantom carriage return
  205. if (/^\r$/.test(m0)) {
  206. break;
  207. }
  208. // broken paser?
  209. throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  210. // un-delimited input
  211. case 3:
  212. // null last value
  213. if (m0 === separator) {
  214. endOfValue();
  215. break;
  216. }
  217. // end of entry
  218. if (m0 === '\n') {
  219. endOfValue();
  220. endOfEntry();
  221. break;
  222. }
  223. // phantom carriage return
  224. if (/^\r$/.test(m0)) {
  225. break;
  226. }
  227. if (m0 === delimiter) {
  228. // non-compliant data
  229. throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  230. }
  231. // broken parser?
  232. throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  233. default:
  234. // shenanigans
  235. throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  236. }
  237. //console.log('val:' + m0 + ' state:' + state);
  238. });
  239. // submit the last entry
  240. // ignore null last line
  241. if(entry.length !== 0) {
  242. endOfValue();
  243. endOfEntry();
  244. }
  245. return data;
  246. },
  247. // a csv-specific line splitter
  248. splitLines: function(csv, options) {
  249. // cache settings
  250. var separator = options.separator;
  251. var delimiter = options.delimiter;
  252. // set initial state if it's missing
  253. if(!options.state.rowNum) {
  254. options.state.rowNum = 1;
  255. }
  256. // clear initial state
  257. var entries = [];
  258. var state = 0;
  259. var entry = '';
  260. var exit = false;
  261. function endOfLine() {
  262. // reset the state
  263. state = 0;
  264. // if 'start' hasn't been met, don't output
  265. if(options.start && options.state.rowNum < options.start) {
  266. // update global state
  267. entry = '';
  268. options.state.rowNum++;
  269. return;
  270. }
  271. if(options.onParseEntry === undefined) {
  272. // onParseEntry hook not set
  273. entries.push(entry);
  274. } else {
  275. var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
  276. // false skips the row, configurable through a hook
  277. if(hookVal !== false) {
  278. entries.push(hookVal);
  279. }
  280. }
  281. // cleanup
  282. entry = '';
  283. // if 'end' is met, stop parsing
  284. if(options.end && options.state.rowNum >= options.end) {
  285. exit = true;
  286. }
  287. // update global state
  288. options.state.rowNum++;
  289. }
  290. // escape regex-specific control chars
  291. var escSeparator = RegExp.escape(separator);
  292. var escDelimiter = RegExp.escape(delimiter);
  293. // compile the regEx str using the custom delimiter/separator
  294. var match = /(D|S|\n|\r|[^DS\r\n]+)/;
  295. var matchSrc = match.source;
  296. matchSrc = matchSrc.replace(/S/g, escSeparator);
  297. matchSrc = matchSrc.replace(/D/g, escDelimiter);
  298. match = RegExp(matchSrc, 'gm');
  299. // put on your fancy pants...
  300. // process control chars individually, use look-ahead on non-control chars
  301. csv.replace(match, function (m0) {
  302. if(exit) {
  303. return;
  304. }
  305. switch (state) {
  306. // the start of a value/entry
  307. case 0:
  308. // null value
  309. if (m0 === separator) {
  310. entry += m0;
  311. state = 0;
  312. break;
  313. }
  314. // opening delimiter
  315. if (m0 === delimiter) {
  316. entry += m0;
  317. state = 1;
  318. break;
  319. }
  320. // end of line
  321. if (m0 === '\n') {
  322. endOfLine();
  323. break;
  324. }
  325. // phantom carriage return
  326. if (/^\r$/.test(m0)) {
  327. break;
  328. }
  329. // un-delimit value
  330. entry += m0;
  331. state = 3;
  332. break;
  333. // delimited input
  334. case 1:
  335. // second delimiter? check further
  336. if (m0 === delimiter) {
  337. entry += m0;
  338. state = 2;
  339. break;
  340. }
  341. // delimited data
  342. entry += m0;
  343. state = 1;
  344. break;
  345. // delimiter found in delimited input
  346. case 2:
  347. // escaped delimiter?
  348. var prevChar = entry.substr(entry.length - 1);
  349. if (m0 === delimiter && prevChar === delimiter) {
  350. entry += m0;
  351. state = 1;
  352. break;
  353. }
  354. // end of value
  355. if (m0 === separator) {
  356. entry += m0;
  357. state = 0;
  358. break;
  359. }
  360. // end of line
  361. if (m0 === '\n') {
  362. endOfLine();
  363. break;
  364. }
  365. // phantom carriage return
  366. if (m0 === '\r') {
  367. break;
  368. }
  369. // broken paser?
  370. throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
  371. // un-delimited input
  372. case 3:
  373. // null value
  374. if (m0 === separator) {
  375. entry += m0;
  376. state = 0;
  377. break;
  378. }
  379. // end of line
  380. if (m0 === '\n') {
  381. endOfLine();
  382. break;
  383. }
  384. // phantom carriage return
  385. if (m0 === '\r') {
  386. break;
  387. }
  388. // non-compliant data
  389. if (m0 === delimiter) {
  390. throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
  391. }
  392. // broken parser?
  393. throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
  394. default:
  395. // shenanigans
  396. throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
  397. }
  398. //console.log('val:' + m0 + ' state:' + state);
  399. });
  400. // submit the last entry
  401. // ignore null last line
  402. if(entry !== '') {
  403. endOfLine();
  404. }
  405. return entries;
  406. },
  407. // a csv entry parser
  408. parseEntry: function(csv, options) {
  409. // cache settings
  410. var separator = options.separator;
  411. var delimiter = options.delimiter;
  412. // set initial state if it's missing
  413. if(!options.state.rowNum) {
  414. options.state.rowNum = 1;
  415. }
  416. if(!options.state.colNum) {
  417. options.state.colNum = 1;
  418. }
  419. // clear initial state
  420. var entry = [];
  421. var state = 0;
  422. var value = '';
  423. function endOfValue() {
  424. if(options.onParseValue === undefined) {
  425. // onParseValue hook not set
  426. entry.push(value);
  427. } else {
  428. var hook = options.onParseValue(value, options.state); // onParseValue Hook
  429. // false skips the value, configurable through a hook
  430. if(hook !== false) {
  431. entry.push(hook);
  432. }
  433. }
  434. // reset the state
  435. value = '';
  436. state = 0;
  437. // update global state
  438. options.state.colNum++;
  439. }
  440. // checked for a cached regEx first
  441. if(!options.match) {
  442. // escape regex-specific control chars
  443. var escSeparator = RegExp.escape(separator);
  444. var escDelimiter = RegExp.escape(delimiter);
  445. // compile the regEx str using the custom delimiter/separator
  446. var match = /(D|S|\n|\r|[^DS\r\n]+)/;
  447. var matchSrc = match.source;
  448. matchSrc = matchSrc.replace(/S/g, escSeparator);
  449. matchSrc = matchSrc.replace(/D/g, escDelimiter);
  450. options.match = RegExp(matchSrc, 'gm');
  451. }
  452. // put on your fancy pants...
  453. // process control chars individually, use look-ahead on non-control chars
  454. csv.replace(options.match, function (m0) {
  455. switch (state) {
  456. // the start of a value
  457. case 0:
  458. // null last value
  459. if (m0 === separator) {
  460. value += '';
  461. endOfValue();
  462. break;
  463. }
  464. // opening delimiter
  465. if (m0 === delimiter) {
  466. state = 1;
  467. break;
  468. }
  469. // skip un-delimited new-lines
  470. if (m0 === '\n' || m0 === '\r') {
  471. break;
  472. }
  473. // un-delimited value
  474. value += m0;
  475. state = 3;
  476. break;
  477. // delimited input
  478. case 1:
  479. // second delimiter? check further
  480. if (m0 === delimiter) {
  481. state = 2;
  482. break;
  483. }
  484. // delimited data
  485. value += m0;
  486. state = 1;
  487. break;
  488. // delimiter found in delimited input
  489. case 2:
  490. // escaped delimiter?
  491. if (m0 === delimiter) {
  492. value += m0;
  493. state = 1;
  494. break;
  495. }
  496. // null value
  497. if (m0 === separator) {
  498. endOfValue();
  499. break;
  500. }
  501. // skip un-delimited new-lines
  502. if (m0 === '\n' || m0 === '\r') {
  503. break;
  504. }
  505. // broken paser?
  506. throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  507. // un-delimited input
  508. case 3:
  509. // null last value
  510. if (m0 === separator) {
  511. endOfValue();
  512. break;
  513. }
  514. // skip un-delimited new-lines
  515. if (m0 === '\n' || m0 === '\r') {
  516. break;
  517. }
  518. // non-compliant data
  519. if (m0 === delimiter) {
  520. throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  521. }
  522. // broken parser?
  523. throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  524. default:
  525. // shenanigans
  526. throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
  527. }
  528. //console.log('val:' + m0 + ' state:' + state);
  529. });
  530. // submit the last value
  531. endOfValue();
  532. return entry;
  533. }
  534. },
  535. /**
  536. * $.csv.toArray(csv)
  537. * Converts a CSV entry string to a javascript array.
  538. *
  539. * @param {Array} csv The string containing the CSV data.
  540. * @param {Object} [options] An object containing user-defined options.
  541. * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
  542. * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
  543. *
  544. * This method deals with simple CSV strings only. It's useful if you only
  545. * need to parse a single entry. If you need to parse more than one line,
  546. * use $.csv2Array instead.
  547. */
  548. toArray: function(csv, options, callback) {
  549. var options = (options !== undefined ? options : {});
  550. var config = {};
  551. config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
  552. config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
  553. config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
  554. var state = (options.state !== undefined ? options.state : {});
  555. // setup
  556. var options = {
  557. delimiter: config.delimiter,
  558. separator: config.separator,
  559. onParseEntry: options.onParseEntry,
  560. onParseValue: options.onParseValue,
  561. state: state
  562. }
  563. var entry = $.csv.parsers.parseEntry(csv, options);
  564. // push the value to a callback if one is defined
  565. if(!config.callback) {
  566. return entry;
  567. } else {
  568. config.callback('', entry);
  569. }
  570. },
  571. /**
  572. * $.csv.toArrays(csv)
  573. * Converts a CSV string to a javascript array.
  574. *
  575. * @param {String} csv The string containing the raw CSV data.
  576. * @param {Object} [options] An object containing user-defined options.
  577. * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
  578. * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
  579. *
  580. * This method deals with multi-line CSV. The breakdown is simple. The first
  581. * dimension of the array represents the line (or entry/row) while the second
  582. * dimension contains the values (or values/columns).
  583. */
  584. toArrays: function(csv, options, callback) {
  585. var options = (options !== undefined ? options : {});
  586. var config = {};
  587. config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
  588. config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
  589. config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
  590. // setup
  591. var data = [];
  592. var options = {
  593. delimiter: config.delimiter,
  594. separator: config.separator,
  595. onParseEntry: options.onParseEntry,
  596. onParseValue: options.onParseValue,
  597. start: options.start,
  598. end: options.end,
  599. state: {
  600. rowNum: 1,
  601. colNum: 1
  602. }
  603. };
  604. // break the data down to lines
  605. data = $.csv.parsers.parse(csv, options);
  606. // push the value to a callback if one is defined
  607. if(!config.callback) {
  608. return data;
  609. } else {
  610. config.callback('', data);
  611. }
  612. },
  613. /**
  614. * $.csv.toObjects(csv)
  615. * Converts a CSV string to a javascript object.
  616. * @param {String} csv The string containing the raw CSV data.
  617. * @param {Object} [options] An object containing user-defined options.
  618. * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
  619. * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
  620. * @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
  621. *
  622. * This method deals with multi-line CSV strings. Where the headers line is
  623. * used as the key for each value per entry.
  624. */
  625. toObjects: function(csv, options, callback) {
  626. var options = (options !== undefined ? options : {});
  627. var config = {};
  628. config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
  629. config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
  630. config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
  631. config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
  632. options.start = 'start' in options ? options.start : 1;
  633. // account for headers
  634. if(config.headers) {
  635. options.start++;
  636. }
  637. if(options.end && config.headers) {
  638. options.end++;
  639. }
  640. // setup
  641. var lines = [];
  642. var data = [];
  643. var options = {
  644. delimiter: config.delimiter,
  645. separator: config.separator,
  646. onParseEntry: options.onParseEntry,
  647. onParseValue: options.onParseValue,
  648. start: options.start,
  649. end: options.end,
  650. state: {
  651. rowNum: 1,
  652. colNum: 1
  653. },
  654. match: false
  655. };
  656. // fetch the headers
  657. var headerOptions = {
  658. delimiter: config.delimiter,
  659. separator: config.separator,
  660. start: 1,
  661. end: 1,
  662. state: {
  663. rowNum:1,
  664. colNum:1
  665. }
  666. }
  667. var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
  668. var headers = $.csv.toArray(headerLine[0], options);
  669. // fetch the data
  670. var lines = $.csv.parsers.splitLines(csv, options);
  671. // reset the state for re-use
  672. options.state.colNum = 1;
  673. if(headers){
  674. options.state.rowNum = 2;
  675. } else {
  676. options.state.rowNum = 1;
  677. }
  678. // convert data to objects
  679. for(var i=0, len=lines.length; i<len; i++) {
  680. var entry = $.csv.toArray(lines[i], options);
  681. var object = {};
  682. for(var j in headers) {
  683. object[headers[j]] = entry[j];
  684. }
  685. data.push(object);
  686. // update row state
  687. options.state.rowNum++;
  688. }
  689. // push the value to a callback if one is defined
  690. if(!config.callback) {
  691. return data;
  692. } else {
  693. config.callback('', data);
  694. }
  695. },
  696. /**
  697. * $.csv.fromArrays(arrays)
  698. * Converts a javascript array to a CSV String.
  699. *
  700. * @param {Array} array An array containing an array of CSV entries.
  701. * @param {Object} [options] An object containing user-defined options.
  702. * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
  703. * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
  704. *
  705. * This method generates a CSV file from an array of arrays (representing entries).
  706. */
  707. fromArrays: function(arrays, options, callback) {
  708. var options = (options !== undefined ? options : {});
  709. var config = {};
  710. config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
  711. config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
  712. config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
  713. config.escaper = 'escaper' in options ? options.escaper : $.csv.defaults.escaper;
  714. config.experimental = 'experimental' in options ? options.experimental : false;
  715. if(!config.experimental) {
  716. throw new Error('not implemented');
  717. }
  718. var output = [];
  719. for(i in arrays) {
  720. output.push(arrays[i]);
  721. }
  722. // push the value to a callback if one is defined
  723. if(!config.callback) {
  724. return output;
  725. } else {
  726. config.callback('', output);
  727. }
  728. },
  729. /**
  730. * $.csv.fromObjects(objects)
  731. * Converts a javascript dictionary to a CSV string.
  732. * @param {Object} objects An array of objects containing the data.
  733. * @param {Object} [options] An object containing user-defined options.
  734. * @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
  735. * @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
  736. *
  737. * This method generates a CSV file from an array of objects (name:value pairs).
  738. * It starts by detecting the headers and adding them as the first line of
  739. * the CSV file, followed by a structured dump of the data.
  740. */
  741. fromObjects2CSV: function(objects, options, callback) {
  742. var options = (options !== undefined ? options : {});
  743. var config = {};
  744. config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
  745. config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
  746. config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
  747. config.experimental = 'experimental' in options ? options.experimental : false;
  748. if(!config.experimental) {
  749. throw new Error('not implemented');
  750. }
  751. var output = [];
  752. for(i in objects) {
  753. output.push(arrays[i]);
  754. }
  755. // push the value to a callback if one is defined
  756. if(!config.callback) {
  757. return output;
  758. } else {
  759. config.callback('', output);
  760. }
  761. }
  762. };
  763. // Maintenance code to maintain backward-compatibility
  764. // Will be removed in release 1.0
  765. $.csvEntry2Array = $.csv.toArray;
  766. $.csv2Array = $.csv.toArrays;
  767. $.csv2Dictionary = $.csv.toObjects;
  768. })( jQuery );