1(function(window, document) { 2 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 6 7// Config 8angular.module('ngCsv.config', []). 9 value('ngCsv.config', { 10 debug: true 11 }). 12 config(['$compileProvider', function($compileProvider){ 13 if (angular.isDefined($compileProvider.urlSanitizationWhitelist)) { 14 $compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/); 15 } else { 16 $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|data):/); 17 } 18 }]); 19 20// Modules 21angular.module('ngCsv.directives', ['ngCsv.services']); 22angular.module('ngCsv.services', []); 23angular.module('ngCsv', 24 [ 25 'ngCsv.config', 26 'ngCsv.services', 27 'ngCsv.directives', 28 'ngSanitize' 29 ]); 30 31// Common.js package manager support (e.g. ComponentJS, WebPack) 32if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports) { 33 module.exports = 'ngCsv'; 34} 35/** 36 * Created by asafdav on 15/05/14. 37 */ 38angular.module('ngCsv.services'). 39 service('CSV', ['$q', function ($q) { 40 41 var EOL = '\r\n'; 42 var BOM = "\ufeff"; 43 44 var specialChars = { 45 '\\t': '\t', 46 '\\b': '\b', 47 '\\v': '\v', 48 '\\f': '\f', 49 '\\r': '\r' 50 }; 51 52 /** 53 * Stringify one field 54 * @param data 55 * @param options 56 * @returns {*} 57 */ 58 this.stringifyField = function (data, options) { 59 if (options.decimalSep === 'locale' && this.isFloat(data)) { 60 return data.toLocaleString(); 61 } 62 63 if (options.decimalSep !== '.' && this.isFloat(data)) { 64 return data.toString().replace('.', options.decimalSep); 65 } 66 67 if (typeof data === 'string') { 68 data = data.replace(/"/g, '""'); // Escape double qoutes 69 70 if (options.quoteStrings || data.indexOf(',') > -1 || data.indexOf('\n') > -1 || data.indexOf('\r') > -1) { 71 data = options.txtDelim + data + options.txtDelim; 72 } 73 74 return data; 75 } 76 77 if (typeof data === 'boolean') { 78 return data ? 'TRUE' : 'FALSE'; 79 } 80 81 return data; 82 }; 83 84 /** 85 * Helper function to check if input is float 86 * @param input 87 * @returns {boolean} 88 */ 89 this.isFloat = function (input) { 90 return +input === input && (!isFinite(input) || Boolean(input % 1)); 91 }; 92 93 /** 94 * Creates a csv from a data array 95 * @param data 96 * @param options 97 * * header - Provide the first row (optional) 98 * * fieldSep - Field separator, default: ',', 99 * * addByteOrderMarker - Add Byte order mark, default(false) 100 * @param callback 101 */ 102 this.stringify = function (data, options) { 103 var def = $q.defer(); 104 105 var that = this; 106 var csv = ""; 107 var csvContent = ""; 108 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; 114 115 encodingArray = []; 116 angular.forEach(options.header, function (title, key) { 117 this.push(that.stringifyField(title, options)); 118 }, encodingArray); 119 120 headerString = encodingArray.join(options.fieldSep ? options.fieldSep : ","); 121 csvContent += headerString + EOL; 122 } 123 124 var arrData = []; 125 126 if (angular.isArray(responseData)) { 127 arrData = responseData; 128 } 129 else if (angular.isFunction(responseData)) { 130 arrData = responseData(); 131 } 132 133 // Check if using keys as labels 134 if (angular.isDefined(options.label) && options.label && typeof options.label === 'boolean') { 135 var labelArray, labelString; 136 137 labelArray = []; 138 angular.forEach(arrData[0], function(value, label) { 139 this.push(that.stringifyField(label, options)); 140 }, labelArray); 141 labelString = labelArray.join(options.fieldSep ? options.fieldSep : ","); 142 csvContent += labelString + EOL; 143 } 144 145 angular.forEach(arrData, function (oldRow, index) { 146 var row = angular.copy(arrData[index]); 147 var dataString, infoArray; 148 149 infoArray = []; 150 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)); 155 }, infoArray); 156 157 dataString = infoArray.join(options.fieldSep ? options.fieldSep : ","); 158 csvContent += index < arrData.length ? dataString + EOL : dataString; 159 }); 160 161 // Add BOM if needed 162 if (options.addByteOrderMarker) { 163 csv += BOM; 164 } 165 166 // Append the content and resolve. 167 csv += csvContent; 168 def.resolve(csv); 169 }); 170 171 if (typeof dataPromise['catch'] === 'function') { 172 dataPromise['catch'](function (err) { 173 def.reject(err); 174 }); 175 } 176 177 return def.promise; 178 }; 179 180 /** 181 * Helper function to check if input is really a special character 182 * @param input 183 * @returns {boolean} 184 */ 185 this.isSpecialChar = function(input){ 186 return specialChars[input] !== undefined; 187 }; 188 189 /** 190 * Helper function to get what the special character was supposed to be 191 * since Angular escapes the first backslash 192 * @param input 193 * @returns {special character string} 194 */
195 this.getSpecialChar = function (input) { 196 return specialChars[input]; 197 }; 198 199 200 }]); 201/** 202 * ng-csv module 203 * Export Javascript's arrays to csv files from the browser 204 * 205 * Author: asafdav - https://github.com/asafdav 206 */ 207angular.module('ngCsv.directives'). 208 directive('ngCsv', ['$parse', '$q', 'CSV', '$document', '$timeout', function ($parse, $q, CSV, $document, $timeout) { 209 return { 210 restrict: 'AC', 211 scope: { 212 data: '&ngCsv', 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", 222 ngClick: '&', 223 charset: '@charset', 224 label: '&csvLabel' 225 }, 226 controller: [ 227 '$scope', 228 '$element', 229 '$attrs', 230 '$transclude', 231 function ($scope, $element, $attrs, $transclude) { 232 $scope.csv = ''; 233 234 if (!angular.isDefined($scope.lazyLoad) || $scope.lazyLoad != "true") { 235 if (angular.isArray($scope.data)) { 236 $scope.$watch("data", function (newValue) { 237 $scope.buildCSV(); 238 }, true); 239 } 240 } 241 242 $scope.getFilename = function () { 243 return $scope.filename || 'download.csv'; 244 }; 245 246 function getBuildCsvOptions() { 247 var options = { 248 txtDelim: $scope.txtDelim ? $scope.txtDelim : '"', 249 decimalSep: $scope.decimalSep ? $scope.decimalSep : '.', 250 quoteStrings: $scope.quoteStrings, 251 addByteOrderMarker: $scope.addByteOrderMarker 252 }; 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); 256 257 options.fieldSep = $scope.fieldSep ? $scope.fieldSep : ","; 258 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; 261 262 return options; 263 } 264 265 /** 266 * Creates the CSV and updates the scope 267 * @returns {*} 268 */ 269 $scope.buildCSV = function () { 270 var deferred = $q.defer(); 271 272 $element.addClass($attrs.ngCsvLoadingClass || 'ng-csv-loading'); 273 274 CSV.stringify($scope.data(), getBuildCsvOptions()).then(function (csv) { 275 $scope.csv = csv; 276 $element.removeClass($attrs.ngCsvLoadingClass || 'ng-csv-loading'); 277 deferred.resolve(csv); 278 }); 279 $scope.$apply(); // Old angular support 280 281 return deferred.promise; 282 }; 283 } 284 ], 285 link: function (scope, element, attrs) { 286 function doClick() { 287 var charset = scope.charset || "utf-8"; 288 var blob = new Blob([scope.csv], { 289 type: "text/csv;charset="+ charset + ";" 290 }); 291 292 if (window.navigator.msSaveOrOpenBlob) { 293 navigator.msSaveBlob(blob, scope.getFilename()); 294 } else { 295 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'); 301 302 $document.find('body').append(downloadContainer); 303 $timeout(function () { 304 downloadLink[0].click(); 305 downloadLink.remove(); 306 }, null); 307 } 308 } 309 310 element.bind('click', function (e) { 311 scope.buildCSV().then(function (csv) { 312 doClick(); 313 }); 314 scope.$apply(); 315 }); 316 } 317 }; 318 }]); 319})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.