1 (function(window, document) {
3 // Create all modules and define dependencies to make sure they exist
4 // and are loaded in the correct order to satisfy dependency injection
5 // before all nested files are concatenated by Grunt
8 angular.module('ngCsv.config', []).
9 value('ngCsv.config', {
12 config(['$compileProvider', function($compileProvider){
13 if (angular.isDefined($compileProvider.urlSanitizationWhitelist)) {
14 $compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/);
16 $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/);
21 angular.module('ngCsv.directives', ['ngCsv.services']);
22 angular.module('ngCsv.services', []);
23 angular.module('ngCsv',
31 // Common.js package manager support (e.g. ComponentJS, WebPack)
32 if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports) {
33 module.exports = 'ngCsv';
36 * Created by asafdav on 15/05/14.
38 angular.module('ngCsv.services').
39 service('CSV', ['$q', function ($q) {
58 this.stringifyField = function (data, options) {
59 if (options.decimalSep === 'locale' && this.isFloat(data)) {
60 return data.toLocaleString();
63 if (options.decimalSep !== '.' && this.isFloat(data)) {
64 return data.toString().replace('.', options.decimalSep);
67 if (typeof data === 'string') {
68 data = data.replace(/"/g, '""'); // Escape double qoutes
70 if (options.quoteStrings || data.indexOf(',') > -1 || data.indexOf('\n') > -1 || data.indexOf('\r') > -1) {
71 data = options.txtDelim + data + options.txtDelim;
77 if (typeof data === 'boolean') {
78 return data ? 'TRUE' : 'FALSE';
85 * Helper function to check if input is float
89 this.isFloat = function (input) {
90 return +input === input && (!isFinite(input) || Boolean(input % 1));
94 * Creates a csv from a data array
97 * * header - Provide the first row (optional)
98 * * fieldSep - Field separator, default: ',',
99 * * addByteOrderMarker - Add Byte order mark, default(false)
102 this.stringify = function (data, options) {
103 var def = $q.defer();
109 var dataPromise = $q.when(data).then(function (responseData) {
110 //responseData = angular.copy(responseData);//moved to row creation
111 // Check if there's a provided header array
112 if (angular.isDefined(options.header) && options.header) {
113 var encodingArray, headerString;
116 angular.forEach(options.header, function (title, key) {
117 this.push(that.stringifyField(title, options));
120 headerString = encodingArray.join(options.fieldSep ? options.fieldSep : ",");
121 csvContent += headerString + EOL;
126 if (angular.isArray(responseData)) {
127 arrData = responseData;
129 else if (angular.isFunction(responseData)) {
130 arrData = responseData();
133 // Check if using keys as labels
134 if (angular.isDefined(options.label) && options.label && typeof options.label === 'boolean') {
135 var labelArray, labelString;
138 angular.forEach(arrData[0], function(value, label) {
139 this.push(that.stringifyField(label, options));
141 labelString = labelArray.join(options.fieldSep ? options.fieldSep : ",");
142 csvContent += labelString + EOL;
145 angular.forEach(arrData, function (oldRow, index) {
146 var row = angular.copy(arrData[index]);
147 var dataString, infoArray;
151 var iterator = !!options.columnOrder ? options.columnOrder : row;
152 angular.forEach(iterator, function (field, key) {
153 var val = !!options.columnOrder ? row[field] : field;
154 this.push(that.stringifyField(val, options));
157 dataString = infoArray.join(options.fieldSep ? options.fieldSep : ",");
158 csvContent += index < arrData.length ? dataString + EOL : dataString;
162 if (options.addByteOrderMarker) {
166 // Append the content and resolve.
171 if (typeof dataPromise['catch'] === 'function') {
172 dataPromise['catch'](function (err) {
181 * Helper function to check if input is really a special character
185 this.isSpecialChar = function(input){
186 return specialChars[input] !== undefined;
190 * Helper function to get what the special character was supposed to be
191 * since Angular escapes the first backslash
193 * @returns {special character string}
195 this.getSpecialChar = function (input) {
196 return specialChars[input];
203 * Export Javascript's arrays to csv files from the browser
205 * Author: asafdav - https://github.com/asafdav
207 angular.module('ngCsv.directives').
208 directive('ngCsv', ['$parse', '$q', 'CSV', '$document', '$timeout', function ($parse, $q, CSV, $document, $timeout) {
213 filename: '@filename',
214 header: '&csvHeader',
215 columnOrder: '&csvColumnOrder',
216 txtDelim: '@textDelimiter',
217 decimalSep: '@decimalSeparator',
218 quoteStrings: '@quoteStrings',
219 fieldSep: '@fieldSeparator',
220 lazyLoad: '@lazyLoad',
221 addByteOrderMarker: "@addBom",
231 function ($scope, $element, $attrs, $transclude) {
234 if (!angular.isDefined($scope.lazyLoad) || $scope.lazyLoad != "true") {
235 if (angular.isArray($scope.data)) {
236 $scope.$watch("data", function (newValue) {
242 $scope.getFilename = function () {
243 return $scope.filename || 'download.csv';
246 function getBuildCsvOptions() {
248 txtDelim: $scope.txtDelim ? $scope.txtDelim : '"',
249 decimalSep: $scope.decimalSep ? $scope.decimalSep : '.',
250 quoteStrings: $scope.quoteStrings,
251 addByteOrderMarker: $scope.addByteOrderMarker
253 if (angular.isDefined($attrs.csvHeader)) options.header = $scope.$eval($scope.header);
254 if (angular.isDefined($attrs.csvColumnOrder)) options.columnOrder = $scope.$eval($scope.columnOrder);
255 if (angular.isDefined($attrs.csvLabel)) options.label = $scope.$eval($scope.label);
257 options.fieldSep = $scope.fieldSep ? $scope.fieldSep : ",";
259 // Replaces any badly formatted special character string with correct special character
260 options.fieldSep = CSV.isSpecialChar(options.fieldSep) ? CSV.getSpecialChar(options.fieldSep) : options.fieldSep;
266 * Creates the CSV and updates the scope
269 $scope.buildCSV = function () {
270 var deferred = $q.defer();
272 $element.addClass($attrs.ngCsvLoadingClass || 'ng-csv-loading');
274 CSV.stringify($scope.data(), getBuildCsvOptions()).then(function (csv) {
276 $element.removeClass($attrs.ngCsvLoadingClass || 'ng-csv-loading');
277 deferred.resolve(csv);
279 $scope.$apply(); // Old angular support
281 return deferred.promise;
285 link: function (scope, element, attrs) {
287 var charset = scope.charset || "utf-8";
288 var blob = new Blob([scope.csv], {
289 type: "text/csv;charset="+ charset + ";"
292 if (window.navigator.msSaveOrOpenBlob) {
293 navigator.msSaveBlob(blob, scope.getFilename());
296 var downloadContainer = angular.element('<div data-tap-disabled="true"><a></a></div>');
297 var downloadLink = angular.element(downloadContainer.children()[0]);
298 downloadLink.attr('href', window.URL.createObjectURL(blob));
299 downloadLink.attr('download', scope.getFilename());
300 downloadLink.attr('target', '_blank');
302 $document.find('body').append(downloadContainer);
303 $timeout(function () {
304 downloadLink[0].click();
305 downloadLink.remove();
310 element.bind('click', function (e) {
311 scope.buildCSV().then(function (csv) {
319 })(window, document);