├── app ├── .gitkeep ├── services │ ├── csv.js │ └── excel.js ├── initializers │ ├── csv.js │ └── excel.js └── components │ ├── export-selector.js │ └── export-selector-onselect.js ├── addon ├── .gitkeep ├── templates │ └── components │ │ ├── export-selector.hbs │ │ └── export-selector-onselect.hbs ├── initializers │ ├── csv.js │ └── excel.js ├── components │ ├── export-selector-onselect.js │ └── export-selector.js ├── utils │ └── utils.js └── services │ ├── csv.js │ └── excel.js ├── vendor ├── .gitkeep ├── FileSaver-1.3.3.js └── Blob.js ├── tests ├── unit │ ├── .gitkeep │ └── services │ │ └── csv-test.js ├── dummy │ ├── app │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── templates │ │ │ ├── components │ │ │ │ └── .gitkeep │ │ │ └── application.hbs │ │ ├── router.js │ │ ├── app.js │ │ └── index.html │ ├── public │ │ ├── robots.txt │ │ └── crossdomain.xml │ └── config │ │ └── environment.js ├── test-helper.js ├── helpers │ ├── resolver.js │ └── start-app.js ├── .jshintrc └── index.html ├── .watchmanconfig ├── .bowerrc ├── blueprints ├── .jshintrc └── ember-cli-data-export │ └── index.js ├── config ├── environment.js └── ember-try.js ├── bower.json ├── .github └── CODEOWNERS ├── .npmignore ├── testem.json ├── .ember-cli ├── .gitignore ├── index.js ├── ember-cli-build.js ├── .editorconfig ├── .jshintrc ├── .travis.yml ├── README.md ├── LICENSE.md └── package.json /app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp"] 3 | } 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/services/csv.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-cli-data-export/services/csv'; 2 | -------------------------------------------------------------------------------- /app/services/excel.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-cli-data-export/services/excel'; 2 | -------------------------------------------------------------------------------- /blueprints/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "console" 4 | ], 5 | "strict": false 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember

2 | 3 | {{outlet}} 4 | -------------------------------------------------------------------------------- /app/initializers/csv.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-cli-data-export/initializers/csv'; 2 | -------------------------------------------------------------------------------- /app/components/export-selector.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-data-export/components/export-selector'; 2 | -------------------------------------------------------------------------------- /app/initializers/excel.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-cli-data-export/initializers/excel'; 2 | -------------------------------------------------------------------------------- /app/components/export-selector-onselect.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-data-export/components/export-selector-onselect'; 2 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-data-export", 3 | "dependencies": { 4 | "jquery": "^1.11.3" 5 | }, 6 | "devDependencies": { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | * @roofstock/ember-cli-data-export-co 4 | -------------------------------------------------------------------------------- /blueprints/ember-cli-data-export/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description: 'Installs the underlying xlsx component using bower', 3 | 4 | normalizeEntityName: function() {} // no-op since we're just adding dependencies 5 | }; 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | tests/ 3 | tmp/ 4 | dist/ 5 | 6 | .bowerrc 7 | .editorconfig 8 | .ember-cli 9 | .travis.yml 10 | .npmignore 11 | **/.gitkeep 12 | bower.json 13 | ember-cli-build.js 14 | Brocfile.js 15 | testem.json 16 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "qunit", 3 | "test_page": "tests/index.html?hidepassed", 4 | "disable_watching": true, 5 | "launch_in_ci": [ 6 | "PhantomJS" 7 | ], 8 | "launch_in_dev": [ 9 | "PhantomJS", 10 | "Chrome" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | var Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | }); 10 | 11 | export default Router; 12 | -------------------------------------------------------------------------------- /addon/templates/components/export-selector.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{select-list content=types value=selectedType action=(action (mut selectedType))}} 3 | 4 |
5 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember/resolver'; 2 | import config from '../../config/environment'; 3 | 4 | var resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /addon/templates/components/export-selector-onselect.hbs: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log 17 | testem.log 18 | .idea/ 19 | -------------------------------------------------------------------------------- /addon/initializers/csv.js: -------------------------------------------------------------------------------- 1 | export function initialize() { 2 | var application = arguments[1] || arguments[0]; 3 | application.inject('route', 'csv', 'service:csv'); 4 | application.inject('component', 'csv', 'service:csv'); 5 | application.inject('controller', 'csv', 'service:csv'); 6 | } 7 | 8 | export default { 9 | name: 'csv', 10 | initialize: initialize 11 | }; 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-cli-data-export', 6 | 7 | included: function(app) { 8 | this._super.included(app); 9 | app.import('vendor/Blob.js'); 10 | app.import('vendor/FileSaver-1.3.3.js'); 11 | app.import('vendor/jszip-0.10.8.js'); 12 | app.import('vendor/xlsx-0.10.8.js'); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /addon/initializers/excel.js: -------------------------------------------------------------------------------- 1 | export function initialize() { 2 | var application = arguments[1] || arguments[0]; 3 | application.inject('route', 'excel', 'service:excel'); 4 | application.inject('component', 'excel', 'service:excel'); 5 | application.inject('controller', 'excel', 'service:excel'); 6 | } 7 | 8 | export default { 9 | name: 'excel', 10 | initialize: initialize 11 | }; 12 | -------------------------------------------------------------------------------- /addon/components/export-selector-onselect.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import layout from '../templates/components/export-selector-onselect'; 3 | 4 | export default Ember.Component.extend({ 5 | layout: layout, 6 | selectPrompt: 'Export File', 7 | 8 | actions: { 9 | triggerExport: function(selectedType) { 10 | this.sendAction('exportData', selectedType); 11 | this.$('#exportTypes').val(''); 12 | } 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /addon/utils/utils.js: -------------------------------------------------------------------------------- 1 | export default function optionize(options, defaultConfig){ 2 | if (Object.prototype.toString.call( options ) === "[object Object]") { 3 | options = options || {}; 4 | } else { 5 | options = {}; 6 | } 7 | 8 | for (let property in defaultConfig) { 9 | options[property] = (typeof options[property] !== 'undefined') ? 10 | options[property] : 11 | defaultConfig[property]; 12 | } 13 | 14 | return options; 15 | } 16 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from 'ember/resolver'; 3 | import loadInitializers from 'ember/load-initializers'; 4 | import config from './config/environment'; 5 | 6 | var App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver: Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /addon/components/export-selector.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import layout from '../templates/components/export-selector'; 3 | 4 | export default Ember.Component.extend({ 5 | layout: layout, 6 | types: Ember.computed(function() { 7 | return [this.get('selectPrompt'), 'Excel', 'CSV']; 8 | }), 9 | selectedType : null, 10 | actionText: 'Export', 11 | selectPrompt: 'Export File', 12 | 13 | actions: { 14 | triggerExport: function() { 15 | this.sendAction('exportData', this.get('selectedType')); 16 | } 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* global require, module */ 2 | var EmberApp = require('ember-cli/lib/broccoli/ember-addon'); 3 | 4 | module.exports = function(defaults) { 5 | var app = new EmberApp(defaults, { 6 | // Add options here 7 | }); 8 | 9 | /* 10 | This build file specifes the options for the dummy test app of this 11 | addon, located in `/tests/dummy` 12 | This build file does *not* influence how the addon or the app using it 13 | behave. You most likely want to be modifying `./index.js` or app's build file 14 | */ 15 | 16 | return app.toTree(); 17 | }; 18 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | var application; 7 | 8 | var attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(function() { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | insert_final_newline = false 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.css] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.html] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.{diff,md}] 34 | trim_trailing_whitespace = false 35 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise", 6 | "saveAs", 7 | "XLSX" 8 | ], 9 | "browser": true, 10 | "boss": true, 11 | "curly": true, 12 | "debug": false, 13 | "devel": true, 14 | "eqeqeq": true, 15 | "evil": true, 16 | "forin": false, 17 | "immed": false, 18 | "laxbreak": false, 19 | "newcap": true, 20 | "noarg": true, 21 | "noempty": false, 22 | "nonew": false, 23 | "nomen": false, 24 | "onevar": false, 25 | "plusplus": false, 26 | "regexp": false, 27 | "undef": true, 28 | "sub": true, 29 | "strict": false, 30 | "white": false, 31 | "eqnull": true, 32 | "esnext": true, 33 | "unused": true 34 | } 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "0.12" 5 | 6 | sudo: false 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | env: 13 | - EMBER_TRY_SCENARIO=default 14 | - EMBER_TRY_SCENARIO=ember-release 15 | - EMBER_TRY_SCENARIO=ember-beta 16 | - EMBER_TRY_SCENARIO=ember-canary 17 | 18 | matrix: 19 | fast_finish: true 20 | allow_failures: 21 | - env: EMBER_TRY_SCENARIO=ember-canary 22 | 23 | before_install: 24 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 25 | - "npm config set spin false" 26 | - "npm install -g npm@^2" 27 | 28 | install: 29 | - npm install -g bower 30 | - npm install 31 | - bower install 32 | 33 | script: 34 | - ember try $EMBER_TRY_SCENARIO test 35 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | 12 | 13 | 14 | 15 | {{content-for 'head-footer'}} 16 | 17 | 18 | {{content-for 'body'}} 19 | 20 | 21 | 22 | 23 | {{content-for 'body-footer'}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | scenarios: [ 3 | { 4 | name: 'default', 5 | dependencies: { } 6 | }, 7 | { 8 | name: 'ember-release', 9 | dependencies: { 10 | 'ember': 'components/ember#release' 11 | }, 12 | resolutions: { 13 | 'ember': 'release' 14 | } 15 | }, 16 | { 17 | name: 'ember-beta', 18 | dependencies: { 19 | 'ember': 'components/ember#beta' 20 | }, 21 | resolutions: { 22 | 'ember': 'beta' 23 | } 24 | }, 25 | { 26 | name: 'ember-canary', 27 | dependencies: { 28 | 'ember': 'components/ember#canary' 29 | }, 30 | resolutions: { 31 | 'ember': 'canary' 32 | } 33 | } 34 | ] 35 | }; 36 | -------------------------------------------------------------------------------- /tests/unit/services/csv-test.js: -------------------------------------------------------------------------------- 1 | import { moduleFor, test } from 'ember-qunit'; 2 | 3 | moduleFor('service:csv', 'Unit | Service | csv', { 4 | // Specify the other units that are required for this test. 5 | // needs: ['service:foo'] 6 | }); 7 | 8 | // Replace this with your real tests. 9 | test('#jsonToCsv - with valid Array of Arrays, it should return an string with CSV format', function(assert) { 10 | const service = this.subject(); 11 | const array = [ 12 | [ 13 | 'name', 14 | 'last_name', 15 | ], 16 | [ 17 | 'Dale', 18 | 'Cooper' 19 | ], 20 | ]; 21 | const expectedCSV = '\"name\",\"last_name\"\r\n\"Dale\",\"Cooper\"\r\n'; 22 | const options = { 23 | separator: ',', 24 | withSeparator: false, 25 | }; 26 | 27 | assert.deepEqual(service.jsonToCsv(array, options), expectedCSV); 28 | }); 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-data-export 2 | 3 | Addon that encapsulates ability to render a data set as either excel or csv. 4 | 5 | ## Installation 6 | 7 | - ember install ember-cli-data-export 8 | 9 | ## Usage 10 | 11 | - uses js-xlsx library for rendering excel content. 12 | - automatically injects a service for both excel and csv format 13 | - feed a datastructure that's an array of arrays, where each internal array is the set of data to be rendered for that row. 14 | - Example: [['Title 1', 'Title 2', 'Title 3'],['row1cell1', 'row1cell2', 'row1cell3'],['row2cell1', 'row2cell2', 'row2cell3']] 15 | 16 | ## Example 17 | ```javascript 18 | 19 | var data = [ 20 | ['Title 1', 'Title 2', 'Title 3'], 21 | ['row1cell1', 'row1cell2', 'row1cell3'], 22 | ['row2cell1', 'row2cell2', 'row2cell3'] 23 | ]; 24 | 25 | if (type === 'Excel') { 26 | this.get('excel').export(data, {sheetName: 'sheet1', fileName: 'test.xlsx'}); 27 | } else if (type === 'CSV') { 28 | this.get('csv').export(data, {fileName: 'test.csv'}); 29 | } 30 | ``` -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName" 25 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": true, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esnext": true, 51 | "unused": true 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | {{content-for 'test-head'}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for 'head-footer'}} 18 | {{content-for 'test-head-footer'}} 19 | 20 | 21 | 22 | {{content-for 'body'}} 23 | {{content-for 'test-body'}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{content-for 'body-footer'}} 32 | {{content-for 'test-body-footer'}} 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | 16 | APP: { 17 | // Here you can pass flags/options to your application instance 18 | // when it is created 19 | } 20 | }; 21 | 22 | if (environment === 'development') { 23 | // ENV.APP.LOG_RESOLVER = true; 24 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 25 | // ENV.APP.LOG_TRANSITIONS = true; 26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 27 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 28 | } 29 | 30 | if (environment === 'test') { 31 | // Testem prefers this... 32 | ENV.baseURL = '/'; 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | } 41 | 42 | if (environment === 'production') { 43 | 44 | } 45 | 46 | return ENV; 47 | }; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-data-export", 3 | "version": "0.1.29", 4 | "author": "Matt Armstrong", 5 | "description": "Provides ability to export a set of json data as excel or csv file.", 6 | "directories": { 7 | "doc": "doc", 8 | "test": "tests" 9 | }, 10 | "scripts": { 11 | "build": "ember build", 12 | "start": "ember server", 13 | "test": "ember try:each" 14 | }, 15 | "repository": "https://github.com/roofstock/ember-cli-data-export", 16 | "engines": { 17 | "node": ">= 4" 18 | }, 19 | "license": "MIT", 20 | "devDependencies": { 21 | "broccoli-asset-rev": "^2.4.5", 22 | "ember-ajax": "^2.4.1", 23 | "ember-cli": "^2.12.0", 24 | "ember-cli-app-version": "^2.0.0", 25 | "ember-cli-dependency-checker": "^1.3.0", 26 | "ember-cli-htmlbars": "^1.2.0", 27 | "ember-cli-htmlbars-inline-precompile": "^0.3.6", 28 | "ember-cli-inject-live-reload": "^1.4.1", 29 | "ember-cli-jshint": "^2.0.1", 30 | "ember-cli-qunit": "^3.0.1", 31 | "ember-cli-release": "1.0.0-beta.2", 32 | "ember-cli-shims": "^1.0.2", 33 | "ember-cli-sri": "^2.1.0", 34 | "ember-cli-test-loader": "^1.1.0", 35 | "ember-cli-uglify": "^1.2.0", 36 | "ember-data": "^2.11.0", 37 | "ember-disable-prototype-extensions": "^1.1.0", 38 | "ember-export-application-global": "^1.0.5", 39 | "ember-load-initializers": "^0.6.0", 40 | "ember-resolver": "^3.0.0", 41 | "ember-source": "~2.12.0", 42 | "loader.js": "^4.0.10" 43 | }, 44 | "keywords": [ 45 | "ember-addon", 46 | "ember-cli", 47 | "xlsx", 48 | "excel", 49 | "csv" 50 | ], 51 | "dependencies": { 52 | "ember-cli-htmlbars": "^1.2.0", 53 | "ember-cli-babel": "^5.1.7", 54 | "ember-select-list": "^0.9.5", 55 | "xlsx": "^0.9.0" 56 | }, 57 | "ember-addon": { 58 | "configPath": "tests/dummy/config" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /addon/services/csv.js: -------------------------------------------------------------------------------- 1 | import Ember from "ember"; 2 | import optionize from "../utils/utils"; 3 | 4 | const defaultConfig = { 5 | fileName: 'export.csv', 6 | separator: ',', 7 | withSeparator: true 8 | }; 9 | 10 | export default Ember.Service.extend({ 11 | 12 | export: function (data, options) { 13 | options = optionize(options, defaultConfig); 14 | 15 | var csv = this.jsonToCsv(data, options); 16 | 17 | saveAs(new Blob([csv],{type:"data:text/csv;charset=utf-8"}), options.fileName); 18 | }, 19 | 20 | jsonToCsv(objArray, options) { 21 | var array = typeof objArray !== 'object' ? JSON.parse(objArray) : objArray; 22 | 23 | var str = ''; 24 | var line = ''; 25 | var value; 26 | 27 | if (options.withSeparator) { 28 | // add separator identifier; 29 | str += `sep=${options.separator}\r\n`; 30 | } 31 | 32 | // add heading row 33 | var head = array[0]; 34 | for (var i = 0; i < head.length; i++) { 35 | value = head[i] + ""; 36 | if (i > 0) { 37 | line += options.separator; 38 | } 39 | line += '"' + value.replace(/"/g, '""') + '"'; 40 | } 41 | 42 | str += line + '\r\n'; 43 | 44 | // add items 45 | for (i = 1; i < array.length; i++) { 46 | line = ''; 47 | 48 | for (var index = 0; index < array[i].length; index++) { 49 | value = array[i][index]; 50 | 51 | if (index > 0) { 52 | line += options.separator; 53 | } 54 | if (typeof value === 'object') { 55 | if (value) { 56 | var resolveValue; 57 | if (value._d instanceof Date) { 58 | // dealing with encoding issue in IE browsers. 59 | resolveValue = (value._d.getMonth() + 1) + '/' + value._d.getDate() + '/' + value._d.getFullYear(); 60 | } 61 | else { 62 | resolveValue = value._d.toString(); 63 | } 64 | 65 | line += '"' + resolveValue.replace(/"/g, '""') + '"'; 66 | } 67 | else { 68 | line += '""'; 69 | } 70 | } 71 | else { 72 | value = value + ""; 73 | if (value && value !== 'undefined') { 74 | line += '"' + value.replace(/"/g, '""') + '"'; 75 | } 76 | else { 77 | line += '""'; 78 | } 79 | } 80 | } 81 | 82 | str += line + '\r\n'; 83 | } 84 | return str; 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /addon/services/excel.js: -------------------------------------------------------------------------------- 1 | import Ember from "ember"; 2 | import optionize from "../utils/utils"; 3 | 4 | const defaultConfig = { 5 | sheetName: 'Sheet1', 6 | fileName: 'export.xlsx' 7 | }; 8 | 9 | export default Ember.Service.extend({ 10 | 11 | export : function(data, options) { 12 | 13 | options = optionize(options, defaultConfig); 14 | 15 | function s2ab(s) { 16 | var buf = new ArrayBuffer(s.length); 17 | var view = new Uint8Array(buf); 18 | for (var i=0; i!==s.length; ++i) { view[i] = s.charCodeAt(i) & 0xFF; } 19 | return buf; 20 | } 21 | 22 | function datenum(v, date1904) { 23 | if(date1904) { v+=1462; } 24 | var epoch = Date.parse(v); 25 | return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); 26 | } 27 | 28 | function sheet_from_array_of_arrays(data) { 29 | var ws = {}; 30 | var range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }}; 31 | for(var R = 0; R !== data.length; ++R) { 32 | for(var C = 0; C !== data[R].length; ++C) { 33 | if(range.s.r > R) { range.s.r = R; } 34 | if(range.s.c > C) { range.s.c = C; } 35 | if(range.e.r < R) { range.e.r = R; } 36 | if(range.e.c < C) { range.e.c = C; } 37 | var cell = {v: data[R][C] }; 38 | if(cell.v == null) { continue; } 39 | var cell_ref = XLSX.utils.encode_cell({c:C,r:R}); 40 | 41 | if(typeof cell.v === 'number') { cell.t = 'n'; } 42 | else if(typeof cell.v === 'boolean') { cell.t = 'b'; } 43 | else if((typeof cell.v === 'object') && (cell.v._d instanceof Date)) { 44 | cell.t = 'n'; cell.z = XLSX.SSF._table[14]; 45 | cell.v = datenum(cell.v._d); 46 | } 47 | else { cell.t = 's'; } 48 | 49 | ws[cell_ref] = cell; 50 | } 51 | } 52 | if(range.s.c < 10000000) { ws['!ref'] = XLSX.utils.encode_range(range); } 53 | return ws; 54 | } 55 | 56 | function Workbook() { 57 | if(!(this instanceof Workbook)) { return new Workbook(); } 58 | this.SheetNames = []; 59 | this.Sheets = {}; 60 | } 61 | 62 | var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); 63 | 64 | /* add worksheet to workbook */ 65 | wb.SheetNames.push(options.sheetName); 66 | wb.Sheets[options.sheetName] = ws; 67 | var wbout = XLSX.write(wb, {bookType:'xlsx', bookSST:true, type: 'binary'}); 68 | 69 | saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), options.fileName); 70 | 71 | } 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /vendor/FileSaver-1.3.3.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 1.3.2 4 | * 2016-06-16 18:25:19 5 | * 6 | * By Eli Grey, http://eligrey.com 7 | * License: MIT 8 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 9 | */ 10 | 11 | /*global self */ 12 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 15 | 16 | var saveAs = saveAs || (function(view) { 17 | "use strict"; 18 | // IE <10 is explicitly unsupported 19 | if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 20 | return; 21 | } 22 | var 23 | doc = view.document 24 | // only get URL when necessary in case Blob.js hasn't overridden it yet 25 | , get_URL = function() { 26 | return view.URL || view.webkitURL || view; 27 | } 28 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 29 | , can_use_save_link = "download" in save_link 30 | , click = function(node) { 31 | var event = new MouseEvent("click"); 32 | node.dispatchEvent(event); 33 | } 34 | , is_safari = /constructor/i.test(view.HTMLElement) || view.safari 35 | , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) 36 | , throw_outside = function(ex) { 37 | (view.setImmediate || view.setTimeout)(function() { 38 | throw ex; 39 | }, 0); 40 | } 41 | , force_saveable_type = "application/octet-stream" 42 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 43 | , arbitrary_revoke_timeout = 1000 * 40 // in ms 44 | , revoke = function(file) { 45 | var revoker = function() { 46 | if (typeof file === "string") { // file is an object URL 47 | get_URL().revokeObjectURL(file); 48 | } else { // file is a File 49 | file.remove(); 50 | } 51 | }; 52 | setTimeout(revoker, arbitrary_revoke_timeout); 53 | } 54 | , dispatch = function(filesaver, event_types, event) { 55 | event_types = [].concat(event_types); 56 | var i = event_types.length; 57 | while (i--) { 58 | var listener = filesaver["on" + event_types[i]]; 59 | if (typeof listener === "function") { 60 | try { 61 | listener.call(filesaver, event || filesaver); 62 | } catch (ex) { 63 | throw_outside(ex); 64 | } 65 | } 66 | } 67 | } 68 | , auto_bom = function(blob) { 69 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 70 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 71 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 72 | return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); 73 | } 74 | return blob; 75 | } 76 | , FileSaver = function(blob, name, no_auto_bom) { 77 | if (!no_auto_bom) { 78 | blob = auto_bom(blob); 79 | } 80 | // First try a.download, then web filesystem, then object URLs 81 | var 82 | filesaver = this 83 | , type = blob.type 84 | , force = type === force_saveable_type 85 | , object_url 86 | , dispatch_all = function() { 87 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 88 | } 89 | // on any filesys errors revert to saving with object URLs 90 | , fs_error = function() { 91 | if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { 92 | // Safari doesn't allow downloading of blob urls 93 | var reader = new FileReader(); 94 | reader.onloadend = function() { 95 | var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); 96 | var popup = view.open(url, '_blank'); 97 | if(!popup) view.location.href = url; 98 | url=undefined; // release reference before dispatching 99 | filesaver.readyState = filesaver.DONE; 100 | dispatch_all(); 101 | }; 102 | reader.readAsDataURL(blob); 103 | filesaver.readyState = filesaver.INIT; 104 | return; 105 | } 106 | // don't create more object URLs than needed 107 | if (!object_url) { 108 | object_url = get_URL().createObjectURL(blob); 109 | } 110 | if (force) { 111 | view.location.href = object_url; 112 | } else { 113 | var opened = view.open(object_url, "_blank"); 114 | if (!opened) { 115 | // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html 116 | view.location.href = object_url; 117 | } 118 | } 119 | filesaver.readyState = filesaver.DONE; 120 | dispatch_all(); 121 | revoke(object_url); 122 | } 123 | ; 124 | filesaver.readyState = filesaver.INIT; 125 | 126 | if (can_use_save_link) { 127 | object_url = get_URL().createObjectURL(blob); 128 | setTimeout(function() { 129 | save_link.href = object_url; 130 | save_link.download = name; 131 | click(save_link); 132 | dispatch_all(); 133 | revoke(object_url); 134 | filesaver.readyState = filesaver.DONE; 135 | }); 136 | return; 137 | } 138 | 139 | fs_error(); 140 | } 141 | , FS_proto = FileSaver.prototype 142 | , saveAs = function(blob, name, no_auto_bom) { 143 | return new FileSaver(blob, name || blob.name || "download", no_auto_bom); 144 | } 145 | ; 146 | // IE 10+ (native saveAs) 147 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 148 | return function(blob, name, no_auto_bom) { 149 | name = name || blob.name || "download"; 150 | 151 | if (!no_auto_bom) { 152 | blob = auto_bom(blob); 153 | } 154 | return navigator.msSaveOrOpenBlob(blob, name); 155 | }; 156 | } 157 | 158 | FS_proto.abort = function(){}; 159 | FS_proto.readyState = FS_proto.INIT = 0; 160 | FS_proto.WRITING = 1; 161 | FS_proto.DONE = 2; 162 | 163 | FS_proto.error = 164 | FS_proto.onwritestart = 165 | FS_proto.onprogress = 166 | FS_proto.onwrite = 167 | FS_proto.onabort = 168 | FS_proto.onerror = 169 | FS_proto.onwriteend = 170 | null; 171 | 172 | return saveAs; 173 | }( 174 | typeof self !== "undefined" && self 175 | || typeof window !== "undefined" && window 176 | || this.content 177 | )); 178 | // `self` is undefined in Firefox for Android content script context 179 | // while `this` is nsIContentFrameMessageManager 180 | // with an attribute `content` that corresponds to the window 181 | 182 | if (typeof module !== "undefined" && module.exports) { 183 | module.exports.saveAs = saveAs; 184 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { 185 | define("FileSaver.js", function() { 186 | return saveAs; 187 | }); 188 | } 189 | -------------------------------------------------------------------------------- /vendor/Blob.js: -------------------------------------------------------------------------------- 1 | /* Blob.js 2 | * A Blob implementation. 3 | * 2014-07-24 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * By Devin Samarin, https://github.com/dsamarin 7 | * License: MIT 8 | * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9 | */ 10 | 11 | /*global self, unescape */ 12 | /*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, 13 | plusplus: true */ 14 | 15 | /*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ 16 | 17 | (function (view) { 18 | "use strict"; 19 | 20 | view.URL = view.URL || view.webkitURL; 21 | 22 | if (view.Blob && view.URL) { 23 | try { 24 | new Blob; 25 | return; 26 | } catch (e) {} 27 | } 28 | 29 | // Internally we use a BlobBuilder implementation to base Blob off of 30 | // in order to support older browsers that only have BlobBuilder 31 | var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { 32 | var 33 | get_class = function(object) { 34 | return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; 35 | } 36 | , FakeBlobBuilder = function BlobBuilder() { 37 | this.data = []; 38 | } 39 | , FakeBlob = function Blob(data, type, encoding) { 40 | this.data = data; 41 | this.size = data.length; 42 | this.type = type; 43 | this.encoding = encoding; 44 | } 45 | , FBB_proto = FakeBlobBuilder.prototype 46 | , FB_proto = FakeBlob.prototype 47 | , FileReaderSync = view.FileReaderSync 48 | , FileException = function(type) { 49 | this.code = this[this.name = type]; 50 | } 51 | , file_ex_codes = ( 52 | "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " 53 | + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" 54 | ).split(" ") 55 | , file_ex_code = file_ex_codes.length 56 | , real_URL = view.URL || view.webkitURL || view 57 | , real_create_object_URL = real_URL.createObjectURL 58 | , real_revoke_object_URL = real_URL.revokeObjectURL 59 | , URL = real_URL 60 | , btoa = view.btoa 61 | , atob = view.atob 62 | 63 | , ArrayBuffer = view.ArrayBuffer 64 | , Uint8Array = view.Uint8Array 65 | 66 | , origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/ 67 | ; 68 | FakeBlob.fake = FB_proto.fake = true; 69 | while (file_ex_code--) { 70 | FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; 71 | } 72 | // Polyfill URL 73 | if (!real_URL.createObjectURL) { 74 | URL = view.URL = function(uri) { 75 | var 76 | uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a") 77 | , uri_origin 78 | ; 79 | uri_info.href = uri; 80 | if (!("origin" in uri_info)) { 81 | if (uri_info.protocol.toLowerCase() === "data:") { 82 | uri_info.origin = null; 83 | } else { 84 | uri_origin = uri.match(origin); 85 | uri_info.origin = uri_origin && uri_origin[1]; 86 | } 87 | } 88 | return uri_info; 89 | }; 90 | } 91 | URL.createObjectURL = function(blob) { 92 | var 93 | type = blob.type 94 | , data_URI_header 95 | ; 96 | if (type === null) { 97 | type = "application/octet-stream"; 98 | } 99 | if (blob instanceof FakeBlob) { 100 | data_URI_header = "data:" + type; 101 | if (blob.encoding === "base64") { 102 | return data_URI_header + ";base64," + blob.data; 103 | } else if (blob.encoding === "URI") { 104 | return data_URI_header + "," + decodeURIComponent(blob.data); 105 | } if (btoa) { 106 | return data_URI_header + ";base64," + btoa(blob.data); 107 | } else { 108 | return data_URI_header + "," + encodeURIComponent(blob.data); 109 | } 110 | } else if (real_create_object_URL) { 111 | return real_create_object_URL.call(real_URL, blob); 112 | } 113 | }; 114 | URL.revokeObjectURL = function(object_URL) { 115 | if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { 116 | real_revoke_object_URL.call(real_URL, object_URL); 117 | } 118 | }; 119 | FBB_proto.append = function(data/*, endings*/) { 120 | var bb = this.data; 121 | // decode data to a binary string 122 | if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { 123 | var 124 | str = "" 125 | , buf = new Uint8Array(data) 126 | , i = 0 127 | , buf_len = buf.length 128 | ; 129 | for (; i < buf_len; i++) { 130 | str += String.fromCharCode(buf[i]); 131 | } 132 | bb.push(str); 133 | } else if (get_class(data) === "Blob" || get_class(data) === "File") { 134 | if (FileReaderSync) { 135 | var fr = new FileReaderSync; 136 | bb.push(fr.readAsBinaryString(data)); 137 | } else { 138 | // async FileReader won't work as BlobBuilder is sync 139 | throw new FileException("NOT_READABLE_ERR"); 140 | } 141 | } else if (data instanceof FakeBlob) { 142 | if (data.encoding === "base64" && atob) { 143 | bb.push(atob(data.data)); 144 | } else if (data.encoding === "URI") { 145 | bb.push(decodeURIComponent(data.data)); 146 | } else if (data.encoding === "raw") { 147 | bb.push(data.data); 148 | } 149 | } else { 150 | if (typeof data !== "string") { 151 | data += ""; // convert unsupported types to strings 152 | } 153 | // decode UTF-16 to binary string 154 | bb.push(unescape(encodeURIComponent(data))); 155 | } 156 | }; 157 | FBB_proto.getBlob = function(type) { 158 | if (!arguments.length) { 159 | type = null; 160 | } 161 | return new FakeBlob(this.data.join(""), type, "raw"); 162 | }; 163 | FBB_proto.toString = function() { 164 | return "[object BlobBuilder]"; 165 | }; 166 | FB_proto.slice = function(start, end, type) { 167 | var args = arguments.length; 168 | if (args < 3) { 169 | type = null; 170 | } 171 | return new FakeBlob( 172 | this.data.slice(start, args > 1 ? end : this.data.length) 173 | , type 174 | , this.encoding 175 | ); 176 | }; 177 | FB_proto.toString = function() { 178 | return "[object Blob]"; 179 | }; 180 | FB_proto.close = function() { 181 | this.size = 0; 182 | delete this.data; 183 | }; 184 | return FakeBlobBuilder; 185 | }(view)); 186 | 187 | view.Blob = function(blobParts, options) { 188 | var type = options ? (options.type || "") : ""; 189 | var builder = new BlobBuilder(); 190 | if (blobParts) { 191 | for (var i = 0, len = blobParts.length; i < len; i++) { 192 | if (Uint8Array && blobParts[i] instanceof Uint8Array) { 193 | builder.append(blobParts[i].buffer); 194 | } 195 | else { 196 | builder.append(blobParts[i]); 197 | } 198 | } 199 | } 200 | var blob = builder.getBlob(type); 201 | if (!blob.slice && blob.webkitSlice) { 202 | blob.slice = blob.webkitSlice; 203 | } 204 | return blob; 205 | }; 206 | 207 | var getPrototypeOf = Object.getPrototypeOf || function(object) { 208 | return object.__proto__; 209 | }; 210 | view.Blob.prototype = getPrototypeOf(new view.Blob()); 211 | }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); 212 | --------------------------------------------------------------------------------