├── .gitignore ├── demo ├── downloadify │ ├── images │ │ └── download.png │ ├── media │ │ └── downloadify.swf │ ├── LICENSE.txt │ ├── src │ │ ├── com │ │ │ └── dynamicflash │ │ │ │ └── util │ │ │ │ ├── tests │ │ │ │ └── Base64Test.as │ │ │ │ └── Base64.as │ │ ├── Downloadify.as │ │ └── downloadify.js │ ├── test.html │ ├── js │ │ ├── downloadify.min.js │ │ └── swfobject.js │ └── README.textile ├── stuff.xml ├── spec.html ├── demo.html ├── jasmine-core │ ├── jasmine.css │ └── json2.js ├── excel-issues-test.html └── text.js ├── Excel ├── Paths.js ├── Drawings │ ├── Chart.js │ ├── Drawing.js │ ├── AbsoluteAnchor.js │ ├── OneCellAnchor.js │ ├── TwoCellAnchor.js │ └── Picture.js ├── Positioning.js ├── ZipWorker.js ├── WorksheetExportWorker.js ├── Drawings.js ├── RelationshipManager.js ├── SharedStrings.js ├── XMLDOM.js ├── util.js ├── Table.js ├── Workbook.js └── Worksheet.js ├── .gitmodules ├── .jshintrc ├── package.json ├── spec ├── lib │ └── jasmine-1.3.1 │ │ ├── MIT.LICENSE │ │ └── jasmine.css ├── Excel │ ├── XMLDOM.js │ └── util.js └── SpecRunner.html ├── License └── MIT.txt ├── README.md ├── Template └── BasicReport.js ├── excel-builder.js └── Gruntfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | buildtools/index.js 2 | /nbproject/private/ 3 | .DS_Store 4 | build 5 | node_modules 6 | jszip.js 7 | -------------------------------------------------------------------------------- /demo/downloadify/images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/excel-builder.js/master/demo/downloadify/images/download.png -------------------------------------------------------------------------------- /demo/downloadify/media/downloadify.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/excel-builder.js/master/demo/downloadify/media/downloadify.swf -------------------------------------------------------------------------------- /Excel/Paths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is mostly a global spot where all of the relationship managers can get and set 3 | * path information from/to. 4 | * @module Excel/Paths 5 | */ 6 | define({}); -------------------------------------------------------------------------------- /Excel/Drawings/Chart.js: -------------------------------------------------------------------------------- 1 | define(['underscore', '../util'], function (_) { 2 | "use strict"; 3 | var Chart = function () { 4 | 5 | }; 6 | _.extend(Chart.prototype, { 7 | 8 | }); 9 | return Chart; 10 | }); -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jszip"] 2 | path = jszip 3 | url = git://github.com/Stuk/jszip.git 4 | [submodule "FileSaver"] 5 | path = FileSaver 6 | url = git://github.com/eligrey/FileSaver.js.git 7 | [submodule "Downloadify"] 8 | path = Downloadify 9 | url = https://github.com/dcneiner/Downloadify 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqnull": true, 4 | "eqeqeq": true, 5 | "undef": true, 6 | "unused": true, 7 | "strict": true, 8 | "camelcase": true, 9 | "es3": true, 10 | "forin": true, 11 | "immed": true, 12 | "indent": 4, 13 | "latedef": true, 14 | "newcap": true, 15 | "noempty": true, 16 | "nonbsp": true, 17 | "globals": { 18 | "jQuery": true, 19 | "define": true, 20 | "require": true, 21 | "window": true, 22 | "document": true 23 | } 24 | } -------------------------------------------------------------------------------- /Excel/Positioning.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 | "use strict"; 3 | return { 4 | /** 5 | * Converts pixel sizes to 'EMU's, which is what Open XML uses. 6 | * 7 | * @todo clean this up. Code borrowed from http://polymathprogrammer.com/2009/10/22/english-metric-units-and-open-xml/, 8 | * but not sure that it's going to be as accurate as it needs to be. 9 | * 10 | * @param int pixels 11 | * @returns int 12 | */ 13 | pixelsToEMUs: function (pixels) { 14 | return Math.round(pixels * 914400 / 96); 15 | } 16 | }; 17 | }); -------------------------------------------------------------------------------- /Excel/ZipWorker.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | onmessage = function(event) { 4 | if (!event.data || !event.data.ziplib) { return; } 5 | 6 | importScripts(event.data.ziplib); 7 | 8 | var zip = new JSZip(); 9 | var files = event.data.files; 10 | for(var path in files) { 11 | var content = files[path]; 12 | path = path.substr(1); 13 | zip.file(path, content, {base64: false}); 14 | }; 15 | postMessage({ 16 | base64: !!event.data.base64 17 | }); 18 | postMessage({ 19 | status: 'done', 20 | data: zip.generate({ 21 | base64: !!event.data.base64 22 | }) 23 | }); 24 | }; 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excel-builder", 3 | "version": "1.0.0", 4 | "description": "An easy way of building Excel files with javascript", 5 | "main": "export.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/stephenliberty/excel-builder.js.git" 12 | }, 13 | "keywords": [ 14 | "excel", 15 | "javascript" 16 | ], 17 | "author": "Stephen Liberty", 18 | "license": "GPLv3", 19 | "bugs": { 20 | "url": "https://github.com/stephenliberty/excel-builder.js/issues" 21 | }, 22 | "homepage": "https://github.com/stephenliberty/excel-builder.js", 23 | "devDependencies": { 24 | "grunt": "~0.4.2", 25 | "grunt-contrib-requirejs": "~0.4.1", 26 | "grunt-contrib-uglify": "*", 27 | "almond": "*", 28 | "grunt-contrib-jshint": "*", 29 | "grunt-contrib-copy": "*" 30 | }, 31 | "dependencies": { 32 | "underscore": "~1.6.0", 33 | "jszip": "~2.1.0", 34 | "requirejs": "~2.1.10" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spec/lib/jasmine-1.3.1/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /License/MIT.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009 Stuart Knightley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /demo/downloadify/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Downloadify: Client Side File Creation 2 | JavaScript + Flash Library 3 | 4 | Copyright (c) 2009 Douglas C. Neiner 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /Excel/WorksheetExportWorker.js: -------------------------------------------------------------------------------- 1 | var requireConfig; 2 | var worksheet; 3 | console = { 4 | log: postMessage 5 | }; 6 | start = function(data) { 7 | require(['Worksheet'], function(Worksheet) { 8 | worksheet = new Worksheet(); 9 | worksheet.importData(data); 10 | postMessage({status: 'sharedStrings', data: worksheet.collectSharedStrings()}); 11 | 12 | }); 13 | }; 14 | 15 | onmessage = function(event) { 16 | var data = event.data; 17 | if (typeof data == 'object') { 18 | switch (data.instruction) { 19 | case "setup": 20 | requireConfig = data.config; 21 | importScripts(data.requireJsPath); 22 | require.config(requireConfig); 23 | postMessage({status: "ready"}); 24 | break; 25 | case "start": 26 | start(data.data); 27 | break; 28 | case "export": 29 | worksheet.setSharedStringCollection({ 30 | strings: data.sharedStrings 31 | }); 32 | postMessage({status: "finished", data: worksheet.toXML().toString()}); 33 | break; 34 | } 35 | } 36 | }; 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![bounties received](https://www.bountysource.com/badge/team?team_id=59027&style=bounties_received) 2 | 3 | excel-builder.js 4 | ================ 5 | 6 | A way to build excel files with javascript 7 | 8 | Documentation at http://excelbuilderjs.com/. This includes a 'cookbook' and some 9 | API documentation. 10 | 11 | Building 12 | -------- 13 | 14 | Install Grunt: 15 | 16 | npm install -g grunt-cli 17 | 18 | Install dependencies: 19 | 20 | npm install 21 | 22 | Combine & uglify: 23 | 24 | grunt requirejs 25 | 26 | Distributables 27 | --------------- 28 | excel-builder.compiled.js -> All files in the EB package + Underscore 29 | 30 | excel-builder.dist.js -> All files in the EB package + Underscore, with no need for external AMD provider (Web worker will not function in this build). 31 | 32 | Contributing 33 | ------------- 34 | 35 | Originally this project was sort of sponsored by a previous company I worked for. Unfortunately now it has no backing, and my time is very limited while I work on side projects to help make ends meet. If you use bountysource or contribute via paypal (to stephen@liberty-irm.com) to open up bounties on issues, it is very, very likely that I will add features and fix issues sooner than later. 36 | 37 | Otherwise, if you have the ability to contribute yourself, please just do so as normal - I'll review and pull changes as they come in as quickly as I can. 38 | -------------------------------------------------------------------------------- /Excel/Drawings/Drawing.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is mostly a global spot where all of the relationship managers can get and set 3 | * path information from/to. 4 | * @module Excel/Drawing 5 | */ 6 | define([ 7 | 'underscore', './AbsoluteAnchor', './OneCellAnchor', './TwoCellAnchor' 8 | ], function (_, AbsoluteAnchor, OneCellAnchor, TwoCellAnchor) { 9 | "use strict"; 10 | /** 11 | * @constructor 12 | */ 13 | var Drawing = function () { 14 | this.id = _.uniqueId('Drawing'); 15 | }; 16 | 17 | _.extend(Drawing.prototype, { 18 | /** 19 | * 20 | * @param {String} type Can be 'absoluteAnchor', 'oneCellAnchor', or 'twoCellAnchor'. 21 | * @param {Object} config Shorthand - pass the created anchor coords that can normally be used to construct it. 22 | * @returns {Anchor} 23 | */ 24 | createAnchor: function (type, config) { 25 | config = config || {}; 26 | config.drawing = this; 27 | switch(type) { 28 | case 'absoluteAnchor': 29 | this.anchor = new AbsoluteAnchor(config); 30 | break; 31 | case 'oneCellAnchor': 32 | this.anchor = new OneCellAnchor(config); 33 | break; 34 | case 'twoCellAnchor': 35 | this.anchor = new TwoCellAnchor(config); 36 | break; 37 | } 38 | return this.anchor; 39 | } 40 | }); 41 | 42 | return Drawing; 43 | }); -------------------------------------------------------------------------------- /demo/stuff.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Excel/Drawings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/Drawings 3 | */ 4 | define(['underscore', './RelationshipManager', './util'], function (_, RelationshipManager, util) { 5 | "use strict"; 6 | var Drawings = function () { 7 | this.drawings = []; 8 | this.relations = new RelationshipManager(); 9 | this.id = _.uniqueId('Drawings'); 10 | }; 11 | 12 | _.extend(Drawings.prototype, { 13 | /** 14 | * Adds a drawing (more likely a subclass of a Drawing) to the 'Drawings' for a particular worksheet. 15 | * 16 | * @param {Drawing} drawing 17 | * @returns {undefined} 18 | */ 19 | addDrawing: function (drawing) { 20 | this.drawings.push(drawing); 21 | }, 22 | getCount: function () { 23 | return this.drawings.length; 24 | }, 25 | toXML: function () { 26 | var doc = util.createXmlDoc(util.schemas.spreadsheetDrawing, 'xdr:wsDr'); 27 | var drawings = doc.documentElement; 28 | drawings.setAttribute('xmlns:xdr', util.schemas.spreadsheetDrawing); 29 | drawings.setAttribute('xmlns:a', util.schemas.drawing); 30 | 31 | for(var i = 0, l = this.drawings.length; i < l; i++) { 32 | 33 | var rId = this.relations.getRelationshipId(this.drawings[i].getMediaData()); 34 | if(!rId) { 35 | rId = this.relations.addRelation(this.drawings[i].getMediaData(), this.drawings[i].getMediaType()); //chart 36 | } 37 | this.drawings[i].setRelationshipId(rId); 38 | drawings.appendChild(this.drawings[i].toXML(doc)); 39 | } 40 | return doc; 41 | } 42 | }); 43 | 44 | return Drawings; 45 | }); -------------------------------------------------------------------------------- /demo/spec.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Jasmine Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 32 | 33 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Excel/RelationshipManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/RelationshipManager 3 | */ 4 | define(['underscore', './util', './Paths'], function (_, util, Paths) { 5 | "use strict"; 6 | var RelationshipManager = function () { 7 | this.relations = {}; 8 | this.lastId = 1; 9 | }; 10 | 11 | _.uniqueId('rId'); //priming 12 | 13 | _.extend(RelationshipManager.prototype, { 14 | 15 | importData: function (data) { 16 | this.relations = data.relations; 17 | this.lastId = data.lastId; 18 | }, 19 | exportData: function () { 20 | return { 21 | relations: this.relations, 22 | lastId: this.lastId 23 | }; 24 | }, 25 | 26 | addRelation: function (object, type) { 27 | this.relations[object.id] = { 28 | id: _.uniqueId('rId'), 29 | schema: util.schemas[type] 30 | }; 31 | return this.relations[object.id].id; 32 | }, 33 | 34 | getRelationshipId: function (object) { 35 | return this.relations[object.id] ? this.relations[object.id].id : null; 36 | }, 37 | 38 | toXML: function () { 39 | var doc = util.createXmlDoc(util.schemas.relationshipPackage, 'Relationships'); 40 | var relationships = doc.documentElement; 41 | 42 | _.each(this.relations, function (data, id) { 43 | var relationship = util.createElement(doc, 'Relationship', [ 44 | ['Id', data.id], 45 | ['Type', data.schema], 46 | ['Target', Paths[id]] 47 | ]); 48 | relationships.appendChild(relationship); 49 | }); 50 | return doc; 51 | } 52 | }); 53 | 54 | return RelationshipManager; 55 | }); -------------------------------------------------------------------------------- /Excel/SharedStrings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/SharedStrings 3 | */ 4 | define(['underscore', './util'], function (_, util) { 5 | "use strict"; 6 | var sharedStrings = function () { 7 | this.strings = {}; 8 | this.stringArray = []; 9 | this.id = _.uniqueId('SharedStrings'); 10 | }; 11 | _.extend(sharedStrings.prototype, { 12 | /** 13 | * Adds a string to the shared string file, and returns the ID of the 14 | * string which can be used to reference it in worksheets. 15 | * 16 | * @param string {String} 17 | * @return int 18 | */ 19 | addString: function (string) { 20 | this.strings[string] = this.stringArray.length; 21 | this.stringArray[this.stringArray.length] = string; 22 | return this.strings[string]; 23 | }, 24 | 25 | exportData: function () { 26 | return this.strings; 27 | }, 28 | 29 | toXML: function () { 30 | var doc = util.createXmlDoc(util.schemas.spreadsheetml, 'sst'); 31 | var sharedStringTable = doc.documentElement; 32 | this.stringArray.reverse(); 33 | var l = this.stringArray.length; 34 | sharedStringTable.setAttribute('count', l); 35 | sharedStringTable.setAttribute('uniqueCount', l); 36 | 37 | var template = doc.createElement('si'); 38 | var templateValue = doc.createElement('t'); 39 | templateValue.appendChild(doc.createTextNode('--placeholder--')); 40 | template.appendChild(templateValue); 41 | var strings = this.stringArray; 42 | 43 | while (l--) { 44 | var clone = template.cloneNode(true); 45 | clone.firstChild.firstChild.nodeValue = strings[l]; 46 | sharedStringTable.appendChild(clone); 47 | } 48 | 49 | return doc; 50 | } 51 | }); 52 | return sharedStrings; 53 | }); -------------------------------------------------------------------------------- /Template/BasicReport.js: -------------------------------------------------------------------------------- 1 | define(['../Excel/Workbook', '../Excel/Table'], function (Workbook, Table) { 2 | var Template = function (worksheetConstructorSettings) { 3 | this.workbook = new Workbook(); 4 | this.stylesheet = this.workbook.getStyleSheet(); 5 | 6 | this.columns = {}; 7 | 8 | this.predefinedStyles = { 9 | 10 | }; 11 | 12 | this.predefinedFormatters = { 13 | date: this.stylesheet.createSimpleFormatter('date'), 14 | currency: this.stylesheet.createFormat({format: "$ #,##0.00;$ #,##0.00;-", font: {color: "FFE9F50A"}}), 15 | header: this.stylesheet.createFormat({ 16 | font: { bold: true, underline: true, color: {theme: 3}}, 17 | alignment: {horizontal: 'center'} 18 | }) 19 | }; 20 | 21 | if(worksheetConstructorSettings != null) { 22 | this.worksheet = this.workbook.createWorksheet(worksheetConstructorSettings); 23 | } 24 | else { 25 | this.worksheet = this.workbook.createWorksheet(); 26 | } 27 | this.workbook.addWorksheet(this.worksheet); 28 | this.worksheet.setPageOrientation('landscape'); 29 | this.table = new Table(); 30 | this.table.styleInfo.themeStyle = "TableStyleLight1"; 31 | this.worksheet.addTable(this.table); 32 | this.workbook.addTable(this.table); 33 | } 34 | $.extend(true, Template.prototype, { 35 | setHeader: function () { 36 | this.worksheet.setHeader.apply(this.worksheet, arguments); 37 | }, 38 | setFooter: function () { 39 | this.worksheet.setFooter.apply(this.worksheet, arguments); 40 | }, 41 | prepare: function () { 42 | return this.workbook; 43 | }, 44 | 45 | setData: function (worksheetData) { 46 | this.worksheet.setData(worksheetData); 47 | this.data = worksheetData; 48 | this.table.setReferenceRange([1, 1], [this.columns.length, worksheetData.length]); 49 | }, 50 | 51 | setColumns: function (columns) { 52 | this.columns = columns; 53 | this.worksheet.setColumns(columns); 54 | this.table.setTableColumns(columns); 55 | this.table.setReferenceRange([1, 1], [this.columns.length, this.data.length]); 56 | }, 57 | 58 | getWorksheet: function () { 59 | return this.worksheet; 60 | } 61 | }); 62 | return Template; 63 | }); 64 | -------------------------------------------------------------------------------- /Excel/Drawings/AbsoluteAnchor.js: -------------------------------------------------------------------------------- 1 | define(['underscore', '../util'], function (_, util) { 2 | "use strict"; 3 | /** 4 | * 5 | * @param {Object} config 6 | * @param {Number} config.x X offset in EMU's 7 | * @param {Number} config.y Y offset in EMU's 8 | * @param {Number} config.width Width in EMU's 9 | * @param {Number} config.height Height in EMU's 10 | * @constructor 11 | */ 12 | var AbsoluteAnchor = function (config) { 13 | this.x = null; 14 | this.y = null; 15 | this.width = null; 16 | this.height = null; 17 | if(config) { 18 | this.setPos(config.x, config.y); 19 | this.setDimensions(config.width, config.height); 20 | } 21 | }; 22 | _.extend(AbsoluteAnchor.prototype, { 23 | /** 24 | * Sets the X and Y offsets. 25 | * 26 | * @param {Number} x 27 | * @param {Number} y 28 | * @returns {undefined} 29 | */ 30 | setPos: function (x, y) { 31 | this.x = x; 32 | this.y = y; 33 | }, 34 | /** 35 | * Sets the width and height of the image. 36 | * 37 | * @param {Number} width 38 | * @param {Number} height 39 | * @returns {undefined} 40 | */ 41 | setDimensions: function (width, height) { 42 | this.width = width; 43 | this.height = height; 44 | }, 45 | toXML: function (xmlDoc, content) { 46 | var root = util.createElement(xmlDoc, 'xdr:absoluteAnchor'); 47 | var pos = util.createElement(xmlDoc, 'xdr:pos'); 48 | pos.setAttribute('x', this.x); 49 | pos.setAttribute('y', this.y); 50 | root.appendChild(pos); 51 | 52 | var dimensions = util.createElement(xmlDoc, 'xdr:ext'); 53 | dimensions.setAttribute('cx', this.width); 54 | dimensions.setAttribute('cy', this.height); 55 | root.appendChild(dimensions); 56 | 57 | root.appendChild(content); 58 | 59 | root.appendChild(util.createElement(xmlDoc, 'xdr:clientData')); 60 | return root; 61 | } 62 | }); 63 | return AbsoluteAnchor; 64 | }); -------------------------------------------------------------------------------- /spec/Excel/XMLDOM.js: -------------------------------------------------------------------------------- 1 | define(['Excel/XMLDOM'], function(XMLDOM) { 2 | describe("basic DOM simulator for web workers", function() { 3 | 4 | describe("XMLDOM", function() { 5 | var nodeName = "arbitraryNodeName"; 6 | var ns = "arbitraryNS"; 7 | it("has a documentElement", function () { 8 | var d = new XMLDOM(ns, nodeName); 9 | expect(d.documentElement).toBeTruthy(); 10 | }); 11 | 12 | it("will have a properly named root node", function () { 13 | var d = new XMLDOM(ns, nodeName); 14 | expect(d.documentElement.nodeName).toEqual(nodeName); 15 | }); 16 | 17 | it("will have the correct namespace", function () { 18 | var d = new XMLDOM(ns, nodeName); 19 | expect(d.documentElement.xmlns).toEqual(ns); 20 | }); 21 | 22 | it("will have the appropriate content", function () { 23 | var d = new XMLDOM(ns, nodeName); 24 | console.log(d); 25 | var foo = d.createElement('foo'); 26 | foo.setAttribute("france", "silly"); 27 | foo.setAttribute("britain", "port"); 28 | var bar = d.createElement('bar'); 29 | bar.setAttribute("georgia", "peaches"); 30 | var baz = d.createElement('baz'); 31 | foo.appendChild(bar); 32 | d.documentElement.appendChild(foo); 33 | d.documentElement.appendChild(baz); 34 | 35 | console.log(d.toXmlString()); 36 | }); 37 | 38 | }); 39 | 40 | describe("XMLDOM.XMLNode", function() { 41 | var nodeName = "arbitraryNodeName"; 42 | var ns = "arbitraryNS"; 43 | 44 | it("will clone properly", function () { 45 | var d = new XMLDOM(ns, nodeName); 46 | var foo = d.createElement('foo'); 47 | var bar = d.createElement('bar'); 48 | 49 | foo.appendChild(bar); 50 | 51 | var baz = foo.cloneNode(true); 52 | bar.setAttribute('joy', true); 53 | 54 | expect(baz.joy).toEqual(undefined); 55 | }); 56 | 57 | }); 58 | 59 | 60 | 61 | }); 62 | 63 | }); -------------------------------------------------------------------------------- /spec/Excel/util.js: -------------------------------------------------------------------------------- 1 | define(['Excel/util'], function(util) { 2 | describe("utility functions", function() { 3 | describe("compilePageDetailPiece", function() { 4 | it("will give back the appropriate string for an instruction object", function() { 5 | var io = {text: "Hello there"}; 6 | var text = util.compilePageDetailPiece(io); 7 | var expected = '&"-,Regular"Hello there'; 8 | expect(text).toEqual(expected); 9 | }); 10 | 11 | it("will give back a string with underline instructions when an instruction object has underline set", function() { 12 | var io = {text: "Hello there", underline: true}; 13 | var text = util.compilePageDetailPiece(io); 14 | var expected = '&"-,Regular"&UHello there'; 15 | expect(text).toEqual(expected); 16 | }); 17 | 18 | it("will give back a string with bold instructions when an instruction object has bold set", function() { 19 | var io = {text: "Hello there", bold: true}; 20 | var text = util.compilePageDetailPiece(io); 21 | var expected = '&"-,Bold"Hello there'; 22 | expect(text).toEqual(expected); 23 | }); 24 | 25 | it("will give back a string with font instructions when an instruction object has a font set", function() { 26 | var io = {text: "Hello there", font: 'Arial'}; 27 | var text = util.compilePageDetailPiece(io); 28 | var expected = '&"Arial,Regular"Hello there'; 29 | expect(text).toEqual(expected); 30 | }); 31 | 32 | it("will build each piece of an array of instructions and return the end result", function() { 33 | var io = [{text: "Hello there", font: 'Arial'}, " - on ", {text: "5/7/9", underline: true}]; 34 | var text = util.compilePageDetailPiece(io); 35 | var expected = '&"Arial,Regular"Hello there&"-,Regular" - on &"-,Regular"&U5/7/9'; 36 | expect(text).toEqual(expected); 37 | }); 38 | }); 39 | describe("positionToLetterRef", function() { 40 | it("will give back the appropriate excel cell coordinate on an x/y position", function() { 41 | expect(util.positionToLetterRef(1,1)).toEqual('A1'); 42 | expect(util.positionToLetterRef(5,50)).toEqual('E50'); 43 | expect(util.positionToLetterRef(50,50)).toEqual('AX50'); 44 | }); 45 | }); 46 | }); 47 | }); -------------------------------------------------------------------------------- /demo/downloadify/src/com/dynamicflash/util/tests/Base64Test.as: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006 Steve Webster 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package com.dynamicflash.util.tests { 23 | 24 | import flexunit.framework.TestCase; 25 | import flexunit.framework.TestSuite; 26 | import flash.utils.ByteArray; 27 | 28 | import com.dynamicflash.util.Base64; 29 | 30 | public class Base64Test extends TestCase { 31 | 32 | public function Base64Test(methodName:String = null) { 33 | super(methodName); 34 | } 35 | 36 | public function testEncode():void { 37 | assertEquals("VGhpcyBpcyBhIHRlc3Q=",Base64.encode("This is a test")); 38 | } 39 | 40 | public function testEncodeDecodeBytes():void { 41 | var obj:Object = {name:"Dynamic Flash", url:"http://dynamicflash.com"}; 42 | var source:ByteArray = new ByteArray(); 43 | source.writeObject(obj); 44 | var encoded:String = Base64.encodeByteArray(source); 45 | var decoded:ByteArray = Base64.decodeToByteArray(encoded); 46 | var obj2:Object = decoded.readObject(); 47 | assertEquals(obj.name, obj2.name); 48 | assertEquals(obj.url, obj2.url); 49 | } 50 | 51 | public function testDecode():void { 52 | assertEquals("This is a test",Base64.decode("VGhpcyBpcyBhIHRlc3Q=")); 53 | } 54 | 55 | public function testEncodeDecode():void { 56 | var string:String = "The quick brown fox jumped over the lazy dogs"; 57 | assertEquals(string, Base64.decode(Base64.encode(string))); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Excel/Drawings/OneCellAnchor.js: -------------------------------------------------------------------------------- 1 | define(['underscore', '../util'], function (_, util) { 2 | "use strict"; 3 | /** 4 | * 5 | * @param {Object} config 6 | * @param {Number} config.x The cell column number that the top left of the picture will start in 7 | * @param {Number} config.y The cell row number that the top left of the picture will start in 8 | * @param {Number} config.width Width in EMU's 9 | * @param {Number} config.height Height in EMU's 10 | * @constructor 11 | */ 12 | var OneCellAnchor = function (config) { 13 | this.x = null; 14 | this.y = null; 15 | this.xOff = null; 16 | this.yOff = null; 17 | this.width = null; 18 | this.height = null; 19 | if(config) { 20 | this.setPos(config.x, config.y, config.xOff, config.yOff); 21 | this.setDimensions(config.width, config.height); 22 | } 23 | }; 24 | _.extend(OneCellAnchor.prototype, { 25 | setPos: function (x, y, xOff, yOff) { 26 | this.x = x; 27 | this.y = y; 28 | if(xOff !== undefined) { 29 | this.xOff = xOff; 30 | } 31 | if(yOff !== undefined) { 32 | this.yOff = yOff; 33 | } 34 | }, 35 | setDimensions: function (width, height) { 36 | this.width = width; 37 | this.height = height; 38 | }, 39 | toXML: function (xmlDoc, content) { 40 | var root = util.createElement(xmlDoc, 'xdr:oneCellAnchor'); 41 | var from = util.createElement(xmlDoc, 'xdr:from'); 42 | var fromCol = util.createElement(xmlDoc, 'xdr:col'); 43 | fromCol.appendChild(xmlDoc.createTextNode(this.x)); 44 | var fromColOff = util.createElement(xmlDoc, 'xdr:colOff'); 45 | fromColOff.appendChild(xmlDoc.createTextNode(this.xOff || 0)); 46 | var fromRow = util.createElement(xmlDoc, 'xdr:row'); 47 | fromRow.appendChild(xmlDoc.createTextNode(this.y)); 48 | var fromRowOff = util.createElement(xmlDoc, 'xdr:rowOff'); 49 | fromRowOff.appendChild(xmlDoc.createTextNode(this.yOff || 0)); 50 | from.appendChild(fromCol); 51 | from.appendChild(fromColOff); 52 | from.appendChild(fromRow); 53 | from.appendChild(fromRowOff); 54 | 55 | root.appendChild(from); 56 | 57 | var dimensions = util.createElement(xmlDoc, 'xdr:ext'); 58 | dimensions.setAttribute('cx', this.width); 59 | dimensions.setAttribute('cy', this.height); 60 | root.appendChild(dimensions); 61 | 62 | root.appendChild(content); 63 | 64 | root.appendChild(util.createElement(xmlDoc, 'xdr:clientData')); 65 | return root; 66 | } 67 | }); 68 | return OneCellAnchor; 69 | }); -------------------------------------------------------------------------------- /spec/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Jasmine Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Excel/Drawings/TwoCellAnchor.js: -------------------------------------------------------------------------------- 1 | define(['underscore', '../util'], function (_, util) { 2 | 'use strict'; 3 | var TwoCellAnchor = function (config) { 4 | this.from = {xOff: 0, yOff: 0}; 5 | this.to = {xOff: 0, yOff: 0}; 6 | if(config) { 7 | this.setFrom(config.from.x, config.from.y, config.to.xOff, config.to.yOff); 8 | this.setTo(config.to.x, config.to.y, config.to.xOff, config.to.yOff); 9 | } 10 | }; 11 | _.extend(TwoCellAnchor.prototype, { 12 | setFrom: function (x, y, xOff, yOff) { 13 | this.from.x = x; 14 | this.from.y = y; 15 | if(xOff !== undefined) { 16 | this.from.xOff = xOff; 17 | } 18 | if(yOff !== undefined) { 19 | this.from.yOff = xOff; 20 | } 21 | }, 22 | setTo: function (x, y, xOff, yOff) { 23 | this.to.x = x; 24 | this.to.y = y; 25 | if(xOff !== undefined) { 26 | this.to.xOff = xOff; 27 | } 28 | if(yOff !== undefined) { 29 | this.to.yOff = xOff; 30 | } 31 | }, 32 | toXML: function (xmlDoc, content) { 33 | var root = util.createElement(xmlDoc, 'xdr:twoCellAnchor'); 34 | 35 | var from = util.createElement(xmlDoc, 'xdr:from'); 36 | var fromCol = util.createElement(xmlDoc, 'xdr:col'); 37 | fromCol.appendChild(xmlDoc.createTextNode(this.from.x)); 38 | var fromColOff = util.createElement(xmlDoc, 'xdr:colOff'); 39 | fromColOff.appendChild(xmlDoc.createTextNode(this.from.xOff)); 40 | var fromRow = util.createElement(xmlDoc, 'xdr:row'); 41 | fromRow.appendChild(xmlDoc.createTextNode(this.from.y)); 42 | var fromRowOff = util.createElement(xmlDoc, 'xdr:rowOff'); 43 | fromRowOff.appendChild(xmlDoc.createTextNode(this.from.yOff)); 44 | 45 | from.appendChild(fromCol); 46 | from.appendChild(fromColOff); 47 | from.appendChild(fromRow); 48 | from.appendChild(fromRowOff); 49 | 50 | var to = util.createElement(xmlDoc, 'xdr:to'); 51 | var toCol = util.createElement(xmlDoc, 'xdr:col'); 52 | toCol.appendChild(xmlDoc.createTextNode(this.to.x)); 53 | var toColOff = util.createElement(xmlDoc, 'xdr:colOff'); 54 | toColOff.appendChild(xmlDoc.createTextNode(this.from.xOff)); 55 | var toRow = util.createElement(xmlDoc, 'xdr:row'); 56 | toRow.appendChild(xmlDoc.createTextNode(this.to.y)); 57 | var toRowOff = util.createElement(xmlDoc, 'xdr:rowOff'); 58 | toRowOff.appendChild(xmlDoc.createTextNode(this.from.yOff)); 59 | 60 | to.appendChild(toCol); 61 | to.appendChild(toColOff); 62 | to.appendChild(toRow); 63 | to.appendChild(toRowOff); 64 | 65 | 66 | root.appendChild(from); 67 | root.appendChild(to); 68 | 69 | root.appendChild(content); 70 | 71 | root.appendChild(util.createElement(xmlDoc, 'xdr:clientData')); 72 | return root; 73 | } 74 | }); 75 | return TwoCellAnchor; 76 | }); -------------------------------------------------------------------------------- /excel-builder.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'require', 3 | 'underscore', 4 | './Excel/Workbook', 5 | 'JSZip' 6 | ], function (require, _, Workbook, JSZip) { 7 | 8 | /** 9 | * @name Excel 10 | * @public 11 | * @author Stephen Liberty 12 | * @requires underscore 13 | * @requires Excel/Workbook 14 | * @requires JSZIP 15 | * @exports excel-builder 16 | */ 17 | var Factory = { 18 | /** 19 | * Creates a new workbook. 20 | */ 21 | createWorkbook: function () { 22 | return new Workbook(); 23 | }, 24 | 25 | /** 26 | * Turns a workbook into a downloadable file. 27 | * @param {Excel/Workbook} workbook The workbook that is being converted 28 | * @param {Object} options 29 | * @param {Boolean} options.base64 Whether to 'return' the generated file as a base64 string 30 | * @param {Function} options.success The callback function to run after workbook creation is successful. 31 | * @param {Function} options.error The callback function to run if there is an error creating the workbook. 32 | * @param {String} options.requireJsPath (Optional) The path to requirejs. Will use the id 'requirejs' to look up the script if not specified. 33 | */ 34 | createFileAsync: function (workbook, options) { 35 | 36 | 37 | workbook.generateFilesAsync({ 38 | success: function (files) { 39 | 40 | var worker = new Worker(require.toUrl('./Excel/ZipWorker.js')); 41 | worker.addEventListener('message', function(event, data) { 42 | if(event.data.status == 'done') { 43 | options.success(event.data.data); 44 | } 45 | }); 46 | worker.postMessage({ 47 | files: files, 48 | ziplib: require.toUrl('JSZip'), 49 | base64: (!options || options.base64 !== false) 50 | }); 51 | }, 52 | error: function () { 53 | options.error(); 54 | } 55 | }); 56 | }, 57 | 58 | /** 59 | * Turns a workbook into a downloadable file. 60 | * @param {Excel/Workbook} workbook The workbook that is being converted 61 | * @param {Object} options - options to modify how the zip is created. See http://stuk.github.io/jszip/#doc_generate_options 62 | */ 63 | createFile: function (workbook, options) { 64 | var zip = new JSZip(); 65 | var files = workbook.generateFiles(); 66 | _.each(files, function (content, path) { 67 | path = path.substr(1); 68 | if(path.indexOf('.xml') !== -1 || path.indexOf('.rel') !== -1) { 69 | zip.file(path, content, {base64: false}); 70 | } else { 71 | zip.file(path, content, {base64: true, binary: true}); 72 | } 73 | }) 74 | return zip.generate(_.defaults(options || {}, { 75 | type: "base64" 76 | })); 77 | } 78 | } 79 | 80 | 81 | return Factory; 82 | }); -------------------------------------------------------------------------------- /demo/downloadify/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Downloadify 5 | 14 | 15 | 16 | 17 | 18 |

Downloadify Example

19 |

More info available at the Github Project Page

20 |
21 |

22 |
23 | 24 |

25 |

26 |
27 | 29 |

30 |

31 | You must have Flash 10 installed to download this file. 32 |

33 |
34 | 35 | 56 |

Downloadify Invoke Script For This Page

57 |
58 | Downloadify.create('downloadify',{
59 |   filename: function(){
60 |     return document.getElementById('filename').value;
61 |   },
62 |   data: function(){ 
63 |     return document.getElementById('data').value;
64 |   },
65 |   onComplete: function(){ 
66 |     alert('Your File Has Been Saved!'); 
67 |   },
68 |   onCancel: function(){ 
69 |     alert('You have cancelled the saving of this file.');
70 |   },
71 |   onError: function(){ 
72 |     alert('You must put something in the File Contents or there will be nothing to save!'); 
73 |   },
74 |   swf: 'media/downloadify.swf',
75 |   downloadImage: 'images/download.png',
76 |   width: 100,
77 |   height: 30,
78 |   transparent: true,
79 |   append: false
80 | });
81 | 
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /demo/downloadify/js/downloadify.min.js: -------------------------------------------------------------------------------- 1 | /* Downloadify 0.2 (c) 2009 by Douglas Neiner. Licensed under the MIT license */ 2 | /* See http://github.com/dcneiner/Downloadify for license and more info */ 3 | (function(){Downloadify=window.Downloadify={queue:{},uid:new Date().getTime(),getTextForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getData();return""},getFileNameForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getFilename();return""},getDataTypeForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getDataType();return""},saveComplete:function(a){var b=Downloadify.queue[a];if(b)b.complete();return true},saveCancel:function(a){var b=Downloadify.queue[a];if(b)b.cancel();return true},saveError:function(a){var b=Downloadify.queue[a];if(b)b.error();return true},addToQueue:function(a){Downloadify.queue[a.queue_name]=a},getUID:function(a){if(a.id=="")a.id='downloadify_'+Downloadify.uid++;return a.id}};Downloadify.create=function(a,b){var c=(typeof(a)=="string"?document.getElementById(a):a);return new Downloadify.Container(c,b)};Downloadify.Container=function(d,e){var f=this;f.el=d;f.enabled=true;f.dataCallback=null;f.filenameCallback=null;f.data=null;f.filename=null;var g=function(){f.options=e;if(!f.options.append)f.el.innerHTML="";f.flashContainer=document.createElement('span');f.el.appendChild(f.flashContainer);f.queue_name=Downloadify.getUID(f.flashContainer);if(typeof(f.options.filename)==="function")f.filenameCallback=f.options.filename;else if(f.options.filename)f.filename=f.options.filename;if(typeof(f.options.data)==="function")f.dataCallback=f.options.data;else if(f.options.data)f.data=f.options.data;var a={queue_name:f.queue_name,width:f.options.width,height:f.options.height};var b={allowScriptAccess:'always'};var c={id:f.flashContainer.id,name:f.flashContainer.id};if(f.options.enabled===false)f.enabled=false;if(f.options.transparent===true)b.wmode="transparent";if(f.options.downloadImage)a.downloadImage=f.options.downloadImage;swfobject.embedSWF(f.options.swf,f.flashContainer.id,f.options.width,f.options.height,"10",null,a,b,c);Downloadify.addToQueue(f)};f.enable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(true);f.enabled=true};f.disable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(false);f.enabled=false};f.getData=function(){if(!f.enabled)return"";if(f.dataCallback)return f.dataCallback();else if(f.data)return f.data;else return""};f.getFilename=function(){if(f.filenameCallback)return f.filenameCallback();else if(f.filename)return f.filename;else return""};f.getDataType=function(){if(f.options.dataType)return f.options.dataType;return"string"};f.complete=function(){if(typeof(f.options.onComplete)==="function")f.options.onComplete()};f.cancel=function(){if(typeof(f.options.onCancel)==="function")f.options.onCancel()};f.error=function(){if(typeof(f.options.onError)==="function")f.options.onError()};g()};Downloadify.defaultOptions={swf:'media/downloadify.swf',downloadImage:'images/download.png',width:100,height:30,transparent:true,append:false,dataType:"string"}})();if(typeof(jQuery)!="undefined"){(function($){$.fn.downloadify=function(b){return this.each(function(){b=$.extend({},Downloadify.defaultOptions,b);var a=Downloadify.create(this,b);$(this).data('Downloadify',a)})}})(jQuery)};if(typeof(MooTools)!='undefined'){Element.implement({downloadify:function(a){a=$merge(Downloadify.defaultOptions,a);return this.store('Downloadify',Downloadify.create(this,a))}})}; -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | module.exports = function(grunt) { 4 | 5 | var compileOptions = { 6 | baseUrl: ".", 7 | name: "buildtools/index", 8 | optimize: "none", 9 | paths: { 10 | "underscore": "node_modules/underscore/underscore", 11 | "JSZip": "jszip" 12 | }, 13 | shim: { 14 | "underscore": { 15 | "exports": '_' 16 | } 17 | } 18 | }; 19 | 20 | var files = grunt.file.expand({ 21 | cwd: './dist', 22 | filter: function (src) { 23 | if(src.indexOf('WorksheetExportWorker') != -1) { 24 | return false; 25 | } 26 | return true; 27 | } 28 | }, '../Excel/**/*.js'); 29 | files = files.map(function (filename) { 30 | return filename.replace('.js', ''); 31 | }) 32 | 33 | files.push('../excel-builder'); 34 | 35 | grunt.file.write('./buildtools/index.js', "define(" + JSON.stringify(files) + ", function () {})"); 36 | 37 | // Project configuration. 38 | grunt.initConfig({ 39 | pkg: grunt.file.readJSON('package.json'), 40 | requirejs: { 41 | compile: { 42 | options: _.defaults({ 43 | out: "dist/<%= pkg.name %>.compiled.js" 44 | }, compileOptions) 45 | }, 46 | dist: { 47 | options: _.defaults({ 48 | wrap: { 49 | startFile: [ 50 | './buildtools/start.js', 51 | './node_modules/almond/almond.js' 52 | ], 53 | endFile: [ 54 | './buildtools/end.js' 55 | ] 56 | }, 57 | out: "dist/<%= pkg.name %>.dist.js" 58 | }, compileOptions) 59 | } 60 | }, 61 | copy: { 62 | jzip: { 63 | src: 'node_modules/jszip/dist/jszip.js', 64 | dest: 'jszip.js' 65 | } 66 | }, 67 | uglify: { 68 | options: { 69 | compress: { 70 | drop_console: true 71 | } 72 | }, 73 | optimize: { 74 | files: { 75 | 'dist/<%= pkg.name %>.compiled.min.js': ['dist/<%= pkg.name %>.compiled.js'], 76 | 'dist/<%= pkg.name %>.dist.min.js': ['dist/<%= pkg.name %>.dist.js'] 77 | } 78 | } 79 | }, 80 | jshint: { 81 | options: { 82 | jshintrc: true 83 | }, 84 | all: { 85 | //http://stackoverflow.com/questions/20695823/grunt-contrib-jshint-ignores-has-no-effect (hence the ! in front of **/*Worker.js 86 | src: ['Excel/**/*.js', '!**/*Worker.js'] 87 | } 88 | } 89 | }); 90 | 91 | 92 | grunt.loadNpmTasks('grunt-contrib-copy'); 93 | grunt.loadNpmTasks('grunt-contrib-requirejs'); 94 | grunt.loadNpmTasks('grunt-contrib-jshint'); 95 | grunt.loadNpmTasks('grunt-contrib-uglify'); 96 | 97 | // Default task(s). 98 | grunt.registerTask('default', ['copy', 'jshint:all', 'requirejs', 'uglify']); 99 | }; 100 | -------------------------------------------------------------------------------- /Excel/Drawings/Picture.js: -------------------------------------------------------------------------------- 1 | define(['./Drawing', 'underscore', '../util'], function (Drawing, _, util) { 2 | "use strict"; 3 | var Picture = function () { 4 | this.media = null; 5 | this.id = _.uniqueId('Picture'); 6 | this.pictureId = util.uniqueId('Picture'); 7 | this.fill = {}; 8 | this.mediaData = null; 9 | }; 10 | // 11 | Picture.prototype = new Drawing(); 12 | 13 | _.extend(Picture.prototype, { 14 | setMedia: function (mediaRef) { 15 | this.mediaData = mediaRef; 16 | }, 17 | setDescription: function (description) { 18 | this.description = description; 19 | }, 20 | setFillType: function (type) { 21 | this.fill.type = type; 22 | }, 23 | setFillConfig: function (config) { 24 | _.extend(this.fill, config); 25 | }, 26 | getMediaType: function () { 27 | return 'image'; 28 | }, 29 | getMediaData: function () { 30 | return this.mediaData; 31 | }, 32 | setRelationshipId: function (rId) { 33 | this.mediaData.rId = rId; 34 | }, 35 | toXML: function (xmlDoc) { 36 | var pictureNode = util.createElement(xmlDoc, 'xdr:pic'); 37 | 38 | var nonVisibleProperties = util.createElement(xmlDoc, 'xdr:nvPicPr'); 39 | 40 | var nameProperties = util.createElement(xmlDoc, 'xdr:cNvPr', [ 41 | ['id', this.pictureId], 42 | ['name', this.mediaData.fileName], 43 | ['descr', this.description || ""] 44 | ]); 45 | nonVisibleProperties.appendChild(nameProperties); 46 | var nvPicProperties = util.createElement(xmlDoc, 'xdr:cNvPicPr'); 47 | nvPicProperties.appendChild(util.createElement(xmlDoc, 'a:picLocks', [ 48 | ['noChangeAspect', '1'], 49 | ['noChangeArrowheads', '1'] 50 | ])); 51 | nonVisibleProperties.appendChild(nvPicProperties); 52 | pictureNode.appendChild(nonVisibleProperties); 53 | var pictureFill = util.createElement(xmlDoc, 'xdr:blipFill'); 54 | pictureFill.appendChild(util.createElement(xmlDoc, 'a:blip', [ 55 | ['xmlns:r', util.schemas.relationships], 56 | ['r:embed', this.mediaData.rId] 57 | ])); 58 | pictureFill.appendChild(util.createElement(xmlDoc, 'a:srcRect')); 59 | var stretch = util.createElement(xmlDoc, 'a:stretch'); 60 | stretch.appendChild(util.createElement(xmlDoc, 'a:fillRect')); 61 | pictureFill.appendChild(stretch); 62 | pictureNode.appendChild(pictureFill); 63 | 64 | var shapeProperties = util.createElement(xmlDoc, 'xdr:spPr', [ 65 | ['bwMode', 'auto'] 66 | ]); 67 | 68 | var transform2d = util.createElement(xmlDoc, 'a:xfrm'); 69 | shapeProperties.appendChild(transform2d); 70 | 71 | var presetGeometry = util.createElement(xmlDoc, 'a:prstGeom', [ 72 | ['prst', 'rect'] 73 | ]); 74 | shapeProperties.appendChild(presetGeometry); 75 | 76 | 77 | 78 | pictureNode.appendChild(shapeProperties); 79 | // 80 | // 81 | // 82 | // 83 | // 84 | // 85 | // 86 | // 87 | // 88 | // 89 | // 90 | // 91 | // 92 | // 93 | // 94 | // 95 | // 96 | // 97 | // 98 | // 99 | return this.anchor.toXML(xmlDoc, pictureNode); 100 | } 101 | }); 102 | return Picture; 103 | }); -------------------------------------------------------------------------------- /Excel/XMLDOM.js: -------------------------------------------------------------------------------- 1 | define(['underscore'], function (_) { 2 | 'use strict'; 3 | var XMLDOM = function (ns, rootNodeName) { 4 | this.documentElement = this.createElement(rootNodeName); 5 | this.documentElement.setAttribute('xmlns', ns); 6 | }; 7 | 8 | _.extend(XMLDOM.prototype, { 9 | createElement: function (name) { 10 | return new XMLDOM.XMLNode({ 11 | nodeName: name 12 | }); 13 | }, 14 | createTextNode: function (text) { 15 | return new XMLDOM.TextNode(text); 16 | }, 17 | toString: function () { 18 | return this.documentElement.toString(); 19 | } 20 | }); 21 | 22 | XMLDOM.Node = function () {}; 23 | XMLDOM.Node.Create = function (config) { 24 | switch(config.type) { 25 | case "XML": 26 | return new XMLDOM.XMLNode(config); 27 | case "TEXT": 28 | return new XMLDOM.TextNode(config.nodeValue); 29 | } 30 | }; 31 | 32 | XMLDOM.TextNode = function (text) { 33 | this.nodeValue = text; 34 | }; 35 | _.extend(XMLDOM.TextNode.prototype, { 36 | toJSON: function () { 37 | return { 38 | nodeValue: this.nodeValue, 39 | type: 'TEXT' 40 | }; 41 | }, 42 | toString: function () { 43 | return _.escape(this.nodeValue); 44 | } 45 | }); 46 | 47 | XMLDOM.XMLNode = function (config) { 48 | this.nodeName = config.nodeName; 49 | this.children = []; 50 | this.nodeValue = config.nodeValue || ""; 51 | this.attributes = {}; 52 | 53 | if(config.children) { 54 | for(var i = 0; i < config.children.length; i++) { 55 | this.appendChild(XMLDOM.Node.Create(config.children[i])); 56 | } 57 | } 58 | 59 | if(config.attributes) { 60 | for(var attr in config.attributes) { 61 | if(config.attributes.hasOwnProperty(attr)) { 62 | this.setAttribute(attr, config.attributes[attr]); 63 | } 64 | } 65 | } 66 | }; 67 | _.extend(XMLDOM.XMLNode.prototype, { 68 | 69 | toString: function () { 70 | var string = "<" + this.nodeName; 71 | var attrs = []; 72 | for(var attr in this.attributes) { 73 | if(this.attributes.hasOwnProperty(attr)) { 74 | attrs.push(attr + "=\""+_.escape(this.attributes[attr])+"\""); 75 | } 76 | } 77 | if (attrs.length > 0){ 78 | string+= " " + attrs.join(" "); 79 | } 80 | 81 | var childContent = ""; 82 | for(var i = 0, l = this.children.length; i < l; i++) { 83 | childContent += this.children[i].toString(); 84 | } 85 | 86 | if (childContent){ 87 | string += ">" + childContent + ""; 88 | } else { 89 | string += "/>"; 90 | } 91 | 92 | return string; 93 | }, 94 | 95 | toJSON: function () { 96 | var children = []; 97 | for(var i = 0, l = this.children.length; i < l; i++) { 98 | children.push(this.children[i].toJSON()); 99 | } 100 | return { 101 | nodeName: this.nodeName, 102 | children: children, 103 | nodeValue: this.nodeValue, 104 | attributes: this.attributes, 105 | type: "XML" 106 | }; 107 | }, 108 | 109 | setAttribute: function (name, val) { 110 | if(val === null) { 111 | delete this.attributes[name]; 112 | delete this[name]; 113 | return; 114 | } 115 | this.attributes[name] = val; 116 | this[name] = val; 117 | }, 118 | setAttributeNS: function (ns, name, val) { 119 | this.setAttribute(name, val); 120 | }, 121 | appendChild: function (child) { 122 | this.children.push(child); 123 | this.firstChild = this.children[0]; 124 | }, 125 | cloneNode: function () { 126 | return new XMLDOM.XMLNode(this.toJSON()); 127 | } 128 | }); 129 | 130 | return XMLDOM; 131 | }); -------------------------------------------------------------------------------- /demo/downloadify/src/com/dynamicflash/util/Base64.as: -------------------------------------------------------------------------------- 1 | /* 2 | Base64 - 1.1.0 3 | 4 | Copyright (c) 2006 Steve Webster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.dynamicflash.util { 25 | 26 | import flash.utils.ByteArray; 27 | 28 | public class Base64 { 29 | 30 | private static const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 31 | 32 | public static const version:String = "1.1.0"; 33 | 34 | public static function encode(data:String):String { 35 | // Convert string to ByteArray 36 | var bytes:ByteArray = new ByteArray(); 37 | bytes.writeUTFBytes(data); 38 | 39 | // Return encoded ByteArray 40 | return encodeByteArray(bytes); 41 | } 42 | 43 | public static function encodeByteArray(data:ByteArray):String { 44 | // Initialise output 45 | var output:String = ""; 46 | 47 | // Create data and output buffers 48 | var dataBuffer:Array; 49 | var outputBuffer:Array = new Array(4); 50 | 51 | // Rewind ByteArray 52 | data.position = 0; 53 | 54 | // while there are still bytes to be processed 55 | while (data.bytesAvailable > 0) { 56 | // Create new data buffer and populate next 3 bytes from data 57 | dataBuffer = new Array(); 58 | for (var i:uint = 0; i < 3 && data.bytesAvailable > 0; i++) { 59 | dataBuffer[i] = data.readUnsignedByte(); 60 | } 61 | 62 | // Convert to data buffer Base64 character positions and 63 | // store in output buffer 64 | outputBuffer[0] = (dataBuffer[0] & 0xfc) >> 2; 65 | outputBuffer[1] = ((dataBuffer[0] & 0x03) << 4) | ((dataBuffer[1]) >> 4); 66 | outputBuffer[2] = ((dataBuffer[1] & 0x0f) << 2) | ((dataBuffer[2]) >> 6); 67 | outputBuffer[3] = dataBuffer[2] & 0x3f; 68 | 69 | // If data buffer was short (i.e not 3 characters) then set 70 | // end character indexes in data buffer to index of '=' symbol. 71 | // This is necessary because Base64 data is always a multiple of 72 | // 4 bytes and is basses with '=' symbols. 73 | for (var j:uint = dataBuffer.length; j < 3; j++) { 74 | outputBuffer[j + 1] = 64; 75 | } 76 | 77 | // Loop through output buffer and add Base64 characters to 78 | // encoded data string for each character. 79 | for (var k:uint = 0; k < outputBuffer.length; k++) { 80 | output += BASE64_CHARS.charAt(outputBuffer[k]); 81 | } 82 | } 83 | 84 | // Return encoded data 85 | return output; 86 | } 87 | 88 | public static function decode(data:String):String { 89 | // Decode data to ByteArray 90 | var bytes:ByteArray = decodeToByteArray(data); 91 | 92 | // Convert to string and return 93 | return bytes.readUTFBytes(bytes.length); 94 | } 95 | 96 | public static function decodeToByteArray(data:String):ByteArray { 97 | // Initialise output ByteArray for decoded data 98 | var output:ByteArray = new ByteArray(); 99 | 100 | // Create data and output buffers 101 | var dataBuffer:Array = new Array(4); 102 | var outputBuffer:Array = new Array(3); 103 | 104 | // While there are data bytes left to be processed 105 | for (var i:uint = 0; i < data.length; i += 4) { 106 | // Populate data buffer with position of Base64 characters for 107 | // next 4 bytes from encoded data 108 | for (var j:uint = 0; j < 4 && i + j < data.length; j++) { 109 | dataBuffer[j] = BASE64_CHARS.indexOf(data.charAt(i + j)); 110 | } 111 | 112 | // Decode data buffer back into bytes 113 | outputBuffer[0] = (dataBuffer[0] << 2) + ((dataBuffer[1] & 0x30) >> 4); 114 | outputBuffer[1] = ((dataBuffer[1] & 0x0f) << 4) + ((dataBuffer[2] & 0x3c) >> 2); 115 | outputBuffer[2] = ((dataBuffer[2] & 0x03) << 6) + dataBuffer[3]; 116 | 117 | // Add all non-padded bytes in output buffer to decoded data 118 | for (var k:uint = 0; k < outputBuffer.length; k++) { 119 | if (dataBuffer[k+1] == 64) break; 120 | output.writeByte(outputBuffer[k]); 121 | } 122 | } 123 | 124 | // Rewind decoded data ByteArray 125 | output.position = 0; 126 | 127 | // Return decoded data 128 | return output; 129 | } 130 | 131 | public function Base64() { 132 | throw new Error("Base64 class is static container only"); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /Excel/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/util 3 | */ 4 | define(['./XMLDOM'], function (XMLDOM) { 5 | "use strict"; 6 | var util = { 7 | 8 | _idSpaces: {}, 9 | 10 | /** 11 | * Returns a number based on a namespace. So, running with 'Picture' will return 1. Run again, you will get 2. Run with 'Foo', you'll get 1. 12 | * @param {String} space 13 | * @returns {Number} 14 | */ 15 | uniqueId: function (space) { 16 | if(!this._idSpaces[space]) { 17 | this._idSpaces[space] = 1; 18 | } 19 | return this._idSpaces[space]++; 20 | }, 21 | 22 | /** 23 | * Attempts to create an XML document. Due to limitations in web workers, 24 | * it may return a 'fake' xml document created from the XMLDOM.js file. 25 | * 26 | * Takes a namespace to start the xml file in, as well as the root element 27 | * of the xml file. 28 | * 29 | * @param {type} ns 30 | * @param {type} base 31 | * @returns {ActiveXObject|@exp;document@pro;implementation@call;createDocument|@new;XMLDOM} 32 | */ 33 | createXmlDoc: function (ns, base) { 34 | if(typeof document === 'undefined') { 35 | return new XMLDOM(ns || null, base, null); 36 | } 37 | if(document.implementation && document.implementation.createDocument) { 38 | return document.implementation.createDocument(ns || null, base, null); 39 | } else if (window.ActiveXObject) { 40 | var doc = new window.ActiveXObject( "Microsoft.XMLDOM" ); 41 | var rootNode = doc.createElement(base); 42 | rootNode.setAttribute('xmlns', ns); 43 | doc.appendChild(rootNode); 44 | return doc; 45 | } 46 | throw "No xml document generator"; 47 | }, 48 | 49 | /** 50 | * Creates an xml node (element). Used to simplify some calls, as IE is 51 | * very particular about namespaces and such. 52 | * 53 | * @param {XMLDOM} doc An xml document (actual DOM or fake DOM, not a string) 54 | * @param {type} name The name of the element 55 | * @param {type} attributes 56 | * @returns {XML Node} 57 | */ 58 | createElement: function (doc, name, attributes) { 59 | var el = doc.createElement(name); 60 | var ie = !el.setAttributeNS; 61 | attributes = attributes || []; 62 | var i = attributes.length; 63 | while (i--) { 64 | if(!ie && attributes[i][0].indexOf('xmlns') !== -1) { 65 | el.setAttributeNS("http://www.w3.org/2000/xmlns/", attributes[i][0], attributes[i][1]); 66 | } else { 67 | el.setAttribute(attributes[i][0], attributes[i][1]); 68 | } 69 | } 70 | return el; 71 | }, 72 | 73 | LETTER_REFS: {}, 74 | 75 | positionToLetterRef: function (x, y) { 76 | var digit = 1, index, num = x, string = "", alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 77 | if(this.LETTER_REFS[x]) { 78 | return this.LETTER_REFS[x].concat(y); 79 | } 80 | while (num > 0) { 81 | num -= Math.pow(26, digit -1); 82 | index = num % Math.pow(26, digit); 83 | num -= index; 84 | index = index / Math.pow(26, digit - 1); 85 | string = alphabet.charAt(index) + string; 86 | digit += 1; 87 | } 88 | this.LETTER_REFS[x] = string; 89 | return string.concat(y); 90 | }, 91 | 92 | schemas: { 93 | 'worksheet': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet', 94 | 'sharedStrings': "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings", 95 | 'stylesheet': "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", 96 | 'relationships': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships', 97 | 'relationshipPackage': "http://schemas.openxmlformats.org/package/2006/relationships", 98 | 'contentTypes': "http://schemas.openxmlformats.org/package/2006/content-types", 99 | 'spreadsheetml': "http://schemas.openxmlformats.org/spreadsheetml/2006/main", 100 | 'markupCompat': "http://schemas.openxmlformats.org/markup-compatibility/2006", 101 | 'x14ac': "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac", 102 | 'officeDocument': "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", 103 | 'package': "http://schemas.openxmlformats.org/package/2006/relationships", 104 | 'table': "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table", 105 | 'spreadsheetDrawing': 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing', 106 | 'drawing': 'http://schemas.openxmlformats.org/drawingml/2006/main', 107 | 'drawingRelationship': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', 108 | 'image': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', 109 | 'chart': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart' 110 | } 111 | }; 112 | 113 | return util; 114 | }); -------------------------------------------------------------------------------- /demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
Click me!
14 | 15 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /demo/downloadify/src/Downloadify.as: -------------------------------------------------------------------------------- 1 | /* 2 | Downloadify: Client Side File Creation 3 | JavaScript + Flash Library 4 | 5 | Version: 0.2 6 | 7 | Copyright (c) 2009 Douglas C. Neiner 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | */ 27 | package { 28 | import flash.system.Security; 29 | import flash.events.Event; 30 | import flash.events.MouseEvent; 31 | import flash.net.FileReference; 32 | import flash.net.FileFilter; 33 | import flash.net.URLRequest; 34 | import flash.display.*; 35 | import flash.utils.ByteArray; 36 | import flash.external.ExternalInterface; 37 | import com.dynamicflash.util.Base64; 38 | 39 | [SWF(backgroundColor="#CCCCCC")] 40 | [SWF(backgroundAlpha=0)] 41 | public class Downloadify extends Sprite { 42 | 43 | private var options:Object, 44 | file:FileReference = new FileReference(), 45 | queue_name:String = "", 46 | 47 | _width:Number = 0, 48 | _height:Number = 0, 49 | 50 | enabled:Boolean = true, 51 | over:Boolean = false, 52 | down:Boolean = false, 53 | 54 | buttonImage:String = "images/download.png", 55 | 56 | button:Loader; 57 | 58 | public function Downloadify() { 59 | Security.allowDomain('*'); 60 | 61 | stage.align = StageAlign.TOP_LEFT; 62 | stage.scaleMode = StageScaleMode.NO_SCALE; 63 | 64 | options = this.root.loaderInfo.parameters; 65 | 66 | queue_name = options.queue_name.toString(); 67 | 68 | _width = options.width; 69 | _height = options.height; 70 | 71 | if(options.downloadImage){ 72 | buttonImage = options.downloadImage; 73 | } 74 | 75 | setupDefaultButton(); 76 | addChild(button); 77 | 78 | this.buttonMode = true; 79 | 80 | this.addEventListener(MouseEvent.CLICK, onMouseClickEvent); 81 | this.addEventListener(MouseEvent.ROLL_OVER , onMouseEnter); 82 | this.addEventListener(MouseEvent.ROLL_OUT , onMouseLeave); 83 | this.addEventListener(MouseEvent.MOUSE_DOWN , onMouseDown); 84 | this.addEventListener(MouseEvent.MOUSE_UP , onMouseUp); 85 | 86 | ExternalInterface.addCallback('setEnabled', setEnabled); 87 | 88 | file.addEventListener(Event.COMPLETE, onSaveComplete); 89 | file.addEventListener(Event.CANCEL, onSaveCancel); 90 | } 91 | 92 | private function setEnabled(isEnabled:Boolean):Boolean { 93 | enabled = isEnabled; 94 | if(enabled === true){ 95 | button.y = 0; 96 | this.buttonMode = true; 97 | } else { 98 | button.y = (-3 * _height); 99 | this.buttonMode = false; 100 | } 101 | return enabled; 102 | } 103 | 104 | private function setupDefaultButton():void { 105 | button = new Loader(); 106 | var urlReq:URLRequest = new URLRequest(buttonImage); 107 | button.load(urlReq); 108 | button.x = 0; 109 | button.y = 0; 110 | } 111 | 112 | 113 | 114 | protected function onMouseEnter(event:Event):void { 115 | if(enabled === true){ 116 | if(down === false) button.y = (-1 * _height); 117 | over = true; 118 | } 119 | } 120 | protected function onMouseLeave(event:Event):void { 121 | if(enabled === true){ 122 | if(down === false) button.y = 0; 123 | over = false; 124 | } 125 | } 126 | protected function onMouseDown(event:Event):void { 127 | if(enabled === true){ 128 | button.y = button.y = (-2 * _height); 129 | down = true; 130 | } 131 | } 132 | protected function onMouseUp(event:Event):void { 133 | if(enabled === true){ 134 | if(over === false){ 135 | button.y = 0; 136 | } else { 137 | button.y = (-1 * _height); 138 | } 139 | down = false; 140 | } 141 | } 142 | 143 | protected function onMouseClickEvent(event:Event):void{ 144 | var theData:String = ExternalInterface.call('Downloadify.getTextForSave',queue_name), 145 | filename:String = ExternalInterface.call('Downloadify.getFileNameForSave',queue_name), 146 | dataType:String = ExternalInterface.call('Downloadify.getDataTypeForSave',queue_name); 147 | 148 | if (dataType == "string" && theData != "") { 149 | file.save(theData, filename); 150 | } else if (dataType == "base64" && theData){ 151 | file.save(Base64.decodeToByteArray(theData), filename); 152 | } else { 153 | onSaveError(); 154 | } 155 | } 156 | 157 | protected function onSaveComplete(event:Event):void{ 158 | trace('Save Complete'); 159 | ExternalInterface.call('Downloadify.saveComplete',queue_name); 160 | } 161 | 162 | protected function onSaveCancel(event:Event):void{ 163 | trace('Save Cancel'); 164 | ExternalInterface.call('Downloadify.saveCancel',queue_name); 165 | } 166 | 167 | protected function onSaveError():void{ 168 | trace('Save Error'); 169 | ExternalInterface.call('Downloadify.saveError',queue_name); 170 | } 171 | 172 | } 173 | } -------------------------------------------------------------------------------- /demo/jasmine-core/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 24 | #HTMLReporter .runningAlert { background-color: #666666; } 25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 28 | #HTMLReporter .passingAlert { background-color: #a6b779; } 29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 30 | #HTMLReporter .failingAlert { background-color: #cf867e; } 31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 32 | #HTMLReporter .results { margin-top: 14px; } 33 | #HTMLReporter #details { display: none; } 34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | #HTMLReporter.showDetails .summary { display: none; } 39 | #HTMLReporter.showDetails #details { display: block; } 40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | #HTMLReporter .summary { margin-top: 14px; } 42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 45 | #HTMLReporter .description + .suite { margin-top: 0; } 46 | #HTMLReporter .suite { margin-top: 14px; } 47 | #HTMLReporter .suite a { color: #333333; } 48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 51 | #HTMLReporter .resultMessage span.result { display: block; } 52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 53 | 54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } 55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 61 | #TrivialReporter .runner.running { background-color: yellow; } 62 | #TrivialReporter .options { text-align: right; font-size: .8em; } 63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 64 | #TrivialReporter .suite .suite { margin: 5px; } 65 | #TrivialReporter .suite.passed { background-color: #dfd; } 66 | #TrivialReporter .suite.failed { background-color: #fdd; } 67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 71 | #TrivialReporter .spec.skipped { background-color: #bbb; } 72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 73 | #TrivialReporter .passed { background-color: #cfc; display: none; } 74 | #TrivialReporter .failed { background-color: #fbb; } 75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 77 | #TrivialReporter .resultMessage .mismatch { color: black; } 78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 83 | -------------------------------------------------------------------------------- /spec/lib/jasmine-1.3.1/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 24 | #HTMLReporter .runningAlert { background-color: #666666; } 25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 28 | #HTMLReporter .passingAlert { background-color: #a6b779; } 29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 30 | #HTMLReporter .failingAlert { background-color: #cf867e; } 31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 32 | #HTMLReporter .results { margin-top: 14px; } 33 | #HTMLReporter #details { display: none; } 34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | #HTMLReporter.showDetails .summary { display: none; } 39 | #HTMLReporter.showDetails #details { display: block; } 40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | #HTMLReporter .summary { margin-top: 14px; } 42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 45 | #HTMLReporter .description + .suite { margin-top: 0; } 46 | #HTMLReporter .suite { margin-top: 14px; } 47 | #HTMLReporter .suite a { color: #333333; } 48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 51 | #HTMLReporter .resultMessage span.result { display: block; } 52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 53 | 54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } 55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 61 | #TrivialReporter .runner.running { background-color: yellow; } 62 | #TrivialReporter .options { text-align: right; font-size: .8em; } 63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 64 | #TrivialReporter .suite .suite { margin: 5px; } 65 | #TrivialReporter .suite.passed { background-color: #dfd; } 66 | #TrivialReporter .suite.failed { background-color: #fdd; } 67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 71 | #TrivialReporter .spec.skipped { background-color: #bbb; } 72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 73 | #TrivialReporter .passed { background-color: #cfc; display: none; } 74 | #TrivialReporter .failed { background-color: #fbb; } 75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 77 | #TrivialReporter .resultMessage .mismatch { color: black; } 78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 83 | -------------------------------------------------------------------------------- /Excel/Table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/Table 3 | */ 4 | define(['underscore', './util'], function (_, util) { 5 | "use strict"; 6 | var Table = function (config) { 7 | _.defaults(this, { 8 | name: "", 9 | displayName: "", 10 | dataCellStyle: null, 11 | dataDfxId: null, 12 | headerRowBorderDxfId: null, 13 | headerRowCellStyle: null, 14 | headerRowCount: 1, 15 | headerRowDxfId: null, 16 | insertRow: false, 17 | insertRowShift: false, 18 | ref: null, 19 | tableBorderDxfId: null, 20 | totalsRowBorderDxfId: null, 21 | totalsRowCellStyle: null, 22 | totalsRowCount: 0, 23 | totalsRowDxfId: null, 24 | tableColumns: [], 25 | autoFilter: null, 26 | sortState: null, 27 | styleInfo: {} 28 | }); 29 | this.initialize(config); 30 | }; 31 | _.extend(Table.prototype, { 32 | 33 | initialize: function (config) { 34 | this.displayName = _.uniqueId("Table"); 35 | this.name = this.displayName; 36 | this.id = this.name; 37 | this.tableId = this.id.replace('Table', ''); 38 | _.extend(this, config); 39 | }, 40 | 41 | setReferenceRange: function (start, end) { 42 | this.ref = [start, end]; 43 | }, 44 | 45 | setTableColumns: function (columns) { 46 | _.each(columns, function (column) { 47 | this.addTableColumn(column); 48 | }, this); 49 | }, 50 | 51 | /** 52 | * Expects an object with the following optional properties: 53 | * name (required) 54 | * dataCellStyle 55 | * dataDxfId 56 | * headerRowCellStyle 57 | * headerRowDxfId 58 | * totalsRowCellStyle 59 | * totalsRowDxfId 60 | * totalsRowFunction 61 | * totalsRowLabel 62 | * columnFormula 63 | * columnFormulaIsArrayType (boolean) 64 | * totalFormula 65 | * totalFormulaIsArrayType (boolean) 66 | */ 67 | addTableColumn: function (column) { 68 | if(_.isString(column)) { 69 | column = { 70 | name: column 71 | }; 72 | } 73 | if(!column.name) { 74 | throw "Invalid argument for addTableColumn - minimum requirement is a name property"; 75 | } 76 | this.tableColumns.push(column); 77 | }, 78 | 79 | /** 80 | * Expects an object with the following properties: 81 | * caseSensitive (boolean) 82 | * dataRange 83 | * columnSort (assumes true) 84 | * sortDirection 85 | * sortRange (defaults to dataRange) 86 | */ 87 | setSortState: function (state) { 88 | this.sortState = state; 89 | }, 90 | 91 | toXML: function () { 92 | var doc = util.createXmlDoc(util.schemas.spreadsheetml, 'table'); 93 | var table = doc.documentElement; 94 | table.setAttribute('id', this.tableId); 95 | table.setAttribute('name', this.name); 96 | table.setAttribute('displayName', this.displayName); 97 | var s = this.ref[0]; 98 | var e = this.ref[1]; 99 | table.setAttribute('ref', util.positionToLetterRef(s[0], s[1]) + ":" + util.positionToLetterRef(e[0], e[1])); 100 | 101 | /** TOTALS **/ 102 | table.setAttribute('totalsRowCount', this.totalsRowCount); 103 | 104 | /** HEADER **/ 105 | table.setAttribute('headerRowCount', this.headerRowCount); 106 | if(this.headerRowDxfId) { 107 | table.setAttribute('headerRowDxfId', this.headerRowDxfId); 108 | } 109 | if(this.headerRowBorderDxfId) { 110 | table.setAttribute('headerRowBorderDxfId', this.headerRowBorderDxfId); 111 | } 112 | 113 | if(!this.ref) { 114 | throw "Needs at least a reference range"; 115 | } 116 | if(!this.autoFilter) { 117 | this.addAutoFilter(this.ref[0], this.ref[1]); 118 | } 119 | 120 | table.appendChild(this.exportAutoFilter(doc)); 121 | 122 | table.appendChild(this.exportTableColumns(doc)); 123 | table.appendChild(this.exportTableStyleInfo(doc)); 124 | return doc; 125 | }, 126 | 127 | exportTableColumns: function (doc) { 128 | var tableColumns = doc.createElement('tableColumns'); 129 | tableColumns.setAttribute('count', this.tableColumns.length); 130 | var tcs = this.tableColumns; 131 | for(var i = 0, l = tcs.length; i < l; i++) { 132 | var tc = tcs[i]; 133 | var tableColumn = doc.createElement('tableColumn'); 134 | tableColumn.setAttribute('id', i + 1); 135 | tableColumn.setAttribute('name', tc.name); 136 | tableColumns.appendChild(tableColumn); 137 | 138 | if(tc.totalsRowFunction) { 139 | tableColumn.setAttribute('totalsRowFunction', tc.totalsRowFunction); 140 | } 141 | if(tc.totalsRowLabel) { 142 | tableColumn.setAttribute('totalsRowLabel', tc.totalsRowLabel); 143 | } 144 | } 145 | return tableColumns; 146 | }, 147 | 148 | exportAutoFilter: function (doc) { 149 | var autoFilter = doc.createElement('autoFilter'); 150 | var s = this.autoFilter[0]; 151 | var e = this.autoFilter[1]; 152 | autoFilter.setAttribute('ref', util.positionToLetterRef(s[0], s[1]) + ":" + util.positionToLetterRef(e[0], e[1] - this.totalsRowCount)); 153 | return autoFilter; 154 | }, 155 | 156 | exportTableStyleInfo: function (doc) { 157 | var ts = this.styleInfo; 158 | var tableStyle = doc.createElement('tableStyleInfo'); 159 | tableStyle.setAttribute('name', ts.themeStyle); 160 | tableStyle.setAttribute('showFirstColumn', ts.showFirstColumn ? "1" : "0"); 161 | tableStyle.setAttribute('showLastColumn', ts.showLastColumn ? "1" : "0"); 162 | tableStyle.setAttribute('showColumnStripes', ts.showColumnStripes ? "1" : "0"); 163 | tableStyle.setAttribute('showRowStripes', ts.showRowStripes ? "1" : "0"); 164 | return tableStyle; 165 | }, 166 | 167 | addAutoFilter: function (startRef, endRef) { 168 | this.autoFilter = [startRef, endRef]; 169 | } 170 | }); 171 | return Table; 172 | }); 173 | -------------------------------------------------------------------------------- /demo/downloadify/src/downloadify.js: -------------------------------------------------------------------------------- 1 | /* 2 | Downloadify: Client Side File Creation 3 | JavaScript + Flash Library 4 | 5 | Version: 0.2 6 | 7 | Copyright (c) 2009 Douglas C. Neiner 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | */ 27 | 28 | (function(){ 29 | Downloadify = window.Downloadify = { 30 | queue: {}, 31 | uid: new Date().getTime(), 32 | getTextForSave: function(queue){ 33 | var obj = Downloadify.queue[queue]; 34 | if(obj) return obj.getData(); 35 | return ""; 36 | }, 37 | getFileNameForSave: function(queue){ 38 | var obj = Downloadify.queue[queue]; 39 | if(obj) return obj.getFilename(); 40 | return ""; 41 | }, 42 | getDataTypeForSave: function(queue){ 43 | var obj = Downloadify.queue[queue]; 44 | if(obj) return obj.getDataType(); 45 | return ""; 46 | }, 47 | saveComplete: function(queue){ 48 | var obj = Downloadify.queue[queue]; 49 | if(obj) obj.complete(); 50 | return true; 51 | }, 52 | saveCancel: function(queue){ 53 | var obj = Downloadify.queue[queue]; 54 | if(obj) obj.cancel(); 55 | return true; 56 | }, 57 | saveError: function(queue){ 58 | var obj = Downloadify.queue[queue]; 59 | if(obj) obj.error(); 60 | return true; 61 | }, 62 | addToQueue: function(container){ 63 | Downloadify.queue[container.queue_name] = container; 64 | }, 65 | // Concept adapted from: http://tinyurl.com/yzsyfto 66 | // SWF object runs off of ID's, so this is the good way to get a unique ID 67 | getUID: function(el){ 68 | if(el.id == "") el.id = 'downloadify_' + Downloadify.uid++; 69 | return el.id; 70 | } 71 | }; 72 | 73 | Downloadify.create = function( idOrDOM, options ){ 74 | var el = (typeof(idOrDOM) == "string" ? document.getElementById(idOrDOM) : idOrDOM ); 75 | return new Downloadify.Container(el, options); 76 | }; 77 | 78 | Downloadify.Container = function(el, options){ 79 | var base = this; 80 | 81 | base.el = el; 82 | base.enabled = true; 83 | base.dataCallback = null; 84 | base.filenameCallback = null; 85 | base.data = null; 86 | base.filename = null; 87 | 88 | var init = function(){ 89 | base.options = options; 90 | 91 | if( !base.options.append ) base.el.innerHTML = ""; 92 | 93 | base.flashContainer = document.createElement('span'); 94 | base.el.appendChild(base.flashContainer); 95 | 96 | base.queue_name = Downloadify.getUID( base.flashContainer ); 97 | 98 | if( typeof(base.options.filename) === "function" ) 99 | base.filenameCallback = base.options.filename; 100 | else if (base.options.filename) 101 | base.filename = base.options.filename; 102 | 103 | if( typeof(base.options.data) === "function" ) 104 | base.dataCallback = base.options.data; 105 | else if (base.options.data) 106 | base.data = base.options.data; 107 | 108 | 109 | var flashVars = { 110 | queue_name: base.queue_name, 111 | width: base.options.width, 112 | height: base.options.height 113 | }; 114 | 115 | var params = { 116 | allowScriptAccess: 'always' 117 | }; 118 | 119 | var attributes = { 120 | id: base.flashContainer.id, 121 | name: base.flashContainer.id 122 | }; 123 | 124 | if(base.options.enabled === false) base.enabled = false; 125 | 126 | if(base.options.transparent === true) params.wmode = "transparent"; 127 | 128 | if(base.options.downloadImage) flashVars.downloadImage = base.options.downloadImage; 129 | 130 | swfobject.embedSWF(base.options.swf, base.flashContainer.id, base.options.width, base.options.height, "10", null, flashVars, params, attributes ); 131 | 132 | Downloadify.addToQueue( base ); 133 | }; 134 | 135 | base.enable = function(){ 136 | var swf = document.getElementById(base.flashContainer.id); 137 | swf.setEnabled(true); 138 | base.enabled = true; 139 | }; 140 | 141 | base.disable = function(){ 142 | var swf = document.getElementById(base.flashContainer.id); 143 | swf.setEnabled(false); 144 | base.enabled = false; 145 | }; 146 | 147 | base.getData = function(){ 148 | if( !base.enabled ) return ""; 149 | if( base.dataCallback ) return base.dataCallback(); 150 | else if (base.data) return base.data; 151 | else return ""; 152 | }; 153 | 154 | base.getFilename = function(){ 155 | if( base.filenameCallback ) return base.filenameCallback(); 156 | else if (base.filename) return base.filename; 157 | else return ""; 158 | }; 159 | 160 | base.getDataType = function(){ 161 | if (base.options.dataType) return base.options.dataType; 162 | return "string"; 163 | }; 164 | 165 | base.complete = function(){ 166 | if( typeof(base.options.onComplete) === "function" ) base.options.onComplete(); 167 | }; 168 | 169 | base.cancel = function(){ 170 | if( typeof(base.options.onCancel) === "function" ) base.options.onCancel(); 171 | }; 172 | 173 | base.error = function(){ 174 | if( typeof(base.options.onError) === "function" ) base.options.onError(); 175 | }; 176 | 177 | init(); 178 | }; 179 | 180 | Downloadify.defaultOptions = { 181 | swf: 'media/downloadify.swf', 182 | downloadImage: 'images/download.png', 183 | width: 100, 184 | height: 30, 185 | transparent: true, 186 | append: false, 187 | dataType: "string" 188 | }; 189 | })(); 190 | 191 | // Support for jQuery 192 | if(typeof(jQuery) != "undefined"){ 193 | (function($){ 194 | $.fn.downloadify = function(options){ 195 | return this.each(function(){ 196 | options = $.extend({}, Downloadify.defaultOptions, options); 197 | var dl = Downloadify.create( this, options); 198 | $(this).data('Downloadify', dl); 199 | }); 200 | }; 201 | })(jQuery); 202 | }; 203 | 204 | /* mootools helper */ 205 | if(typeof(MooTools) != 'undefined'){ 206 | Element.implement({ 207 | downloadify: function(options) { 208 | options = $merge(Downloadify.defaultOptions,options); 209 | return this.store('Downloadify',Downloadify.create(this,options)); 210 | } 211 | }); 212 | }; 213 | 214 | -------------------------------------------------------------------------------- /demo/downloadify/README.textile: -------------------------------------------------------------------------------- 1 | h2. Downloadify: Client Side File Creation 2 | 3 | *_Important! The swf has been compiled for online use only. Testing from the file path (i.e. file:// ) will not work as it will violate the security sandbox._* 4 | 5 | h3. Overview 6 | 7 | This library is a tiny JavaScript + Flash library that allows you to generate files on the fly, in the browser, without server interaction. Web applications that allow you to generate vCards, color palettes, custom code, etc would benefit from using this library. In addition to increasing speed (no round trip to the server) this solution can reduce the database and server load of existing web applications. _This is not a library to 'force download' a file from a server. It does not interact with a server at all._ 8 | 9 | h3. Demo 10 | 11 | A "very simple demo is available":http://pixelgraphics.us/downloadify/test.html that lets you supply your own content and filename and test out saving, canceling, and the error functionality when the file is blank. 12 | 13 | For a real world usage, see "Starter for jQuery":http://starter.pixelgraphics.us . To quickly demo the usage, just click "Load Example Data" then click Download. After the initial page load, no further contact is made with the server. Everything happens on the client side. 14 | 15 | h3. Download 16 | 17 | Please download from the "Downloads":http://github.com/dcneiner/Downloadify/downloads section of this project. 18 | 19 | h3. Support 20 | 21 | For support, please use the "Issues":http://github.com/dcneiner/Downloadify/issues section of this project. You can also try to hit me up on "Twitter at @dougneiner":http://twitter.com/dougneiner but I might just ask you to file an issue. :) 22 | 23 | h3. Dependencies 24 | 25 | _The end user must have Flash 10 or higher installed for this plugin to work. As of September 2009, it was at a 93% saturation, so most users should already have it installed._ 26 | 27 | Downloadify is only dependent on "SWFObject 2.0":http://code.google.com/p/swfobject/ which is included with the download. It is compatible with any JavaScript framework but has a helper for both jQuery and MooTools. Neither helper will be activatd if Downloadify is inserted prior to including the framework library. 28 | 29 | h3. Usage 30 | 31 | *Any JavaScript framework*: 32 | 33 |
Downloadify.create( id_or_DOM_element, options );
34 | 35 | *jQuery*: 36 | 37 |
$("#element").downloadify( options );
38 | 39 | *MooTools:* 40 | 41 |
$("elementid").downloadify( options );
42 | 43 | h3. Options 44 | 45 | Unless you are using the jQuery plugin included with Downloadify, you must supply all required options with your call to the Downloadify.create function. 46 | 47 | *Defaults and Required Options* 48 | 49 | * swf: 'media/downloadify.swf' *Required*
_Path to the SWF File. Can be relative from the page, or an absolute path._ 50 | * downloadImage: 'images/download.png' *Required*
_Path to the Button Image. Can be relative from the page or an absolute path._ 51 | * width: 175 *Required*
_Width of the button (and the flash movie)_ 52 | * height: 55 *Required*
_Height of the button. This will be 1/4 the height of the image._ 53 | * filename: *Required*
_Can be a String or a function callback. If a function, the return value of the function will be used as the filename._ 54 | * data: *Required*
_Can be a normal String, base64 encoded String, or a function callback. If a function, the return value of the function will be used as the file data._ 55 | * dataType: 'string'
_Must be a String with the value string or base64. Data paired with the dataType of base64 will be decoded into a ByteArray prior to saving._ 56 | * transparent: false
_Set this to true to use wmode=transparent on the flash movie._ 57 | * append: false
_By default the contents of the targeted DOM element are removed. Set this to true to keep the contents and append the button._ 58 | 59 | *Event Callbacks* 60 | 61 | No data is passed into these functions, they are simply called. 62 | 63 | * onError: _Called when the Download button is clicked but your data callback returns ""._ 64 | * onCancel: _Called when the Download button is clicked but the user then cancels without saving._ 65 | * onComplete: _Called when the Download button is clicked and the file is saved to the user's computer._ 66 | 67 | h3. Setting Up the Image 68 | 69 | Downloadify supports (i.e. requires) three button states with limited support for a fourth. The states are: 70 | 71 | * *Normal* 72 | * *Over* ( When the mouse hovers over the button ) 73 | * *Down* ( When the button is pressed ) 74 | * *Disabled* ( Limited support, when .disable() is called on the Downloadify.Container object ) 75 | 76 | In trying to keep this plugin as simple as possible, all four states are always assumed to be present. You should prepare your button image as a single image the width you want your button, and four times the height of the button. All four states should then live in that one image in the same order as the previous list from top to bottom. 77 | 78 | h3. Potential Issues 79 | 80 | When working with different button images, you may find Flash has cached your image. This is the desired behavior on a live site, but not during development. To get around this, simply supply a ?rev=1 or ?rev=2 etc on the end of your downloadImage url. 81 | 82 | h3. Compiling Notes 83 | 84 | I develop locally using Xcode and the Flex 4 SDK Beta 2. Please do not submit request on how to setup a local testing environment. If you are interested in my Xcode project files, send me a message. 85 | 86 | h3. Developers 87 | 88 | *Core Developer:* "Doug Neiner":http://dougneiner.com 89 | 90 | *Contributors:* 91 | 92 | * "David Walsh":http://davidwalsh.name -- Contributed the MooTools helper 93 | 94 | h3. Change Log 95 | 96 | * *Version 0.2*: 97 | ** Added support for base64 via the "as3base64 Library":http://github.com/spjwebster/as3base64 98 | ** Added dataType option 99 | ** Added MooTools helper (Thanks David!) 100 | ** Upgraded SWFObject to v2.2 101 | * *Original Release:* Version 0.1 102 | 103 | h3. License Information: MIT 104 | 105 | as3base64: "Copyright (c) 2006 Steve Webster":http://github.com/spjwebster/as3base64 106 | 107 | All Downloadify Code: Copyright (c) 2009 Douglas C. Neiner 108 | 109 | 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: 110 | 111 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 112 | 113 | 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. -------------------------------------------------------------------------------- /demo/excel-issues-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
 15 | 			merge cells issue
 16 | 			
17 | worksheet name length issue 18 |
19 | 20 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /demo/downloadify/js/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab/im, 16 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 17 | hasLocation = typeof location !== 'undefined' && location.href, 18 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 19 | defaultHostName = hasLocation && location.hostname, 20 | defaultPort = hasLocation && (location.port || undefined), 21 | buildMap = [], 22 | masterConfig = (module.config && module.config()) || {}, 23 | text, fs; 24 | 25 | text = { 26 | version: '2.0.1', 27 | 28 | strip: function (content) { 29 | //Strips declarations so that external SVG and XML 30 | //documents can be added to a document without worry. Also, if the string 31 | //is an HTML document, only the part inside the body tag is returned. 32 | if (content) { 33 | content = content.replace(xmlRegExp, ""); 34 | var matches = content.match(bodyRegExp); 35 | if (matches) { 36 | content = matches[1]; 37 | } 38 | } else { 39 | content = ""; 40 | } 41 | return content; 42 | }, 43 | 44 | jsEscape: function (content) { 45 | return content.replace(/(['\\])/g, '\\$1') 46 | .replace(/[\f]/g, "\\f") 47 | .replace(/[\b]/g, "\\b") 48 | .replace(/[\n]/g, "\\n") 49 | .replace(/[\t]/g, "\\t") 50 | .replace(/[\r]/g, "\\r") 51 | .replace(/[\u2028]/g, "\\u2028") 52 | .replace(/[\u2029]/g, "\\u2029"); 53 | }, 54 | 55 | createXhr: masterConfig.createXhr || function () { 56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 57 | var xhr, i, progId; 58 | if (typeof XMLHttpRequest !== "undefined") { 59 | return new XMLHttpRequest(); 60 | } else if (typeof ActiveXObject !== "undefined") { 61 | for (i = 0; i < 3; i += 1) { 62 | progId = progIds[i]; 63 | try { 64 | xhr = new ActiveXObject(progId); 65 | } catch (e) {} 66 | 67 | if (xhr) { 68 | progIds = [progId]; // so faster next time 69 | break; 70 | } 71 | } 72 | } 73 | 74 | return xhr; 75 | }, 76 | 77 | /** 78 | * Parses a resource name into its component parts. Resource names 79 | * look like: module/name.ext!strip, where the !strip part is 80 | * optional. 81 | * @param {String} name the resource name 82 | * @returns {Object} with properties "moduleName", "ext" and "strip" 83 | * where strip is a boolean. 84 | */ 85 | parseName: function (name) { 86 | var strip = false, index = name.indexOf("."), 87 | modName = name.substring(0, index), 88 | ext = name.substring(index + 1, name.length); 89 | 90 | index = ext.indexOf("!"); 91 | if (index !== -1) { 92 | //Pull off the strip arg. 93 | strip = ext.substring(index + 1, ext.length); 94 | strip = strip === "strip"; 95 | ext = ext.substring(0, index); 96 | } 97 | 98 | return { 99 | moduleName: modName, 100 | ext: ext, 101 | strip: strip 102 | }; 103 | }, 104 | 105 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 106 | 107 | /** 108 | * Is an URL on another domain. Only works for browser use, returns 109 | * false in non-browser environments. Only used to know if an 110 | * optimized .js version of a text resource should be loaded 111 | * instead. 112 | * @param {String} url 113 | * @returns Boolean 114 | */ 115 | useXhr: function (url, protocol, hostname, port) { 116 | var match = text.xdRegExp.exec(url), 117 | uProtocol, uHostName, uPort; 118 | if (!match) { 119 | return true; 120 | } 121 | uProtocol = match[2]; 122 | uHostName = match[3]; 123 | 124 | uHostName = uHostName.split(':'); 125 | uPort = uHostName[1]; 126 | uHostName = uHostName[0]; 127 | 128 | return (!uProtocol || uProtocol === protocol) && 129 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && 130 | ((!uPort && !uHostName) || uPort === port); 131 | }, 132 | 133 | finishLoad: function (name, strip, content, onLoad) { 134 | content = strip ? text.strip(content) : content; 135 | if (masterConfig.isBuild) { 136 | buildMap[name] = content; 137 | } 138 | onLoad(content); 139 | }, 140 | 141 | load: function (name, req, onLoad, config) { 142 | //Name has format: some.module.filext!strip 143 | //The strip part is optional. 144 | //if strip is present, then that means only get the string contents 145 | //inside a body tag in an HTML string. For XML/SVG content it means 146 | //removing the declarations so the content can be inserted 147 | //into the current doc without problems. 148 | 149 | // Do not bother with the work if a build and text will 150 | // not be inlined. 151 | if (config.isBuild && !config.inlineText) { 152 | onLoad(); 153 | return; 154 | } 155 | 156 | masterConfig.isBuild = config.isBuild; 157 | 158 | var parsed = text.parseName(name), 159 | nonStripName = parsed.moduleName + '.' + parsed.ext, 160 | url = req.toUrl(nonStripName), 161 | useXhr = (masterConfig.useXhr) || 162 | text.useXhr; 163 | 164 | //Load the text. Use XHR if possible and in a browser. 165 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 166 | text.get(url, function (content) { 167 | text.finishLoad(name, parsed.strip, content, onLoad); 168 | }, function (err) { 169 | if (onLoad.error) { 170 | onLoad.error(err); 171 | } 172 | }); 173 | } else { 174 | //Need to fetch the resource across domains. Assume 175 | //the resource has been optimized into a JS module. Fetch 176 | //by the module name + extension, but do not include the 177 | //!strip part to avoid file system issues. 178 | req([nonStripName], function (content) { 179 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 180 | parsed.strip, content, onLoad); 181 | }); 182 | } 183 | }, 184 | 185 | write: function (pluginName, moduleName, write, config) { 186 | if (buildMap.hasOwnProperty(moduleName)) { 187 | var content = text.jsEscape(buildMap[moduleName]); 188 | write.asModule(pluginName + "!" + moduleName, 189 | "define(function () { return '" + 190 | content + 191 | "';});\n"); 192 | } 193 | }, 194 | 195 | writeFile: function (pluginName, moduleName, req, write, config) { 196 | var parsed = text.parseName(moduleName), 197 | nonStripName = parsed.moduleName + '.' + parsed.ext, 198 | //Use a '.js' file name so that it indicates it is a 199 | //script that can be loaded across domains. 200 | fileName = req.toUrl(parsed.moduleName + '.' + 201 | parsed.ext) + '.js'; 202 | 203 | //Leverage own load() method to load plugin value, but only 204 | //write out values that do not have the strip argument, 205 | //to avoid any potential issues with ! in file names. 206 | text.load(nonStripName, req, function (value) { 207 | //Use own write() method to construct full module value. 208 | //But need to create shell that translates writeFile's 209 | //write() to the right interface. 210 | var textWrite = function (contents) { 211 | return write(fileName, contents); 212 | }; 213 | textWrite.asModule = function (moduleName, contents) { 214 | return write.asModule(moduleName, fileName, contents); 215 | }; 216 | 217 | text.write(pluginName, nonStripName, textWrite, config); 218 | }, config); 219 | } 220 | }; 221 | 222 | if (typeof process !== "undefined" && 223 | process.versions && 224 | !!process.versions.node) { 225 | //Using special require.nodeRequire, something added by r.js. 226 | fs = require.nodeRequire('fs'); 227 | 228 | text.get = function (url, callback) { 229 | var file = fs.readFileSync(url, 'utf8'); 230 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 231 | if (file.indexOf('\uFEFF') === 0) { 232 | file = file.substring(1); 233 | } 234 | callback(file); 235 | }; 236 | } else if (text.createXhr()) { 237 | text.get = function (url, callback, errback) { 238 | var xhr = text.createXhr(); 239 | xhr.open('GET', url, true); 240 | 241 | //Allow overrides specified in config 242 | if (masterConfig.onXhr) { 243 | masterConfig.onXhr(xhr, url); 244 | } 245 | 246 | xhr.onreadystatechange = function (evt) { 247 | var status, err; 248 | //Do not explicitly handle errors, those should be 249 | //visible via console output in the browser. 250 | if (xhr.readyState === 4) { 251 | status = xhr.status; 252 | if (status > 399 && status < 600) { 253 | //An http 4xx or 5xx error. Signal an error. 254 | err = new Error(url + ' HTTP status: ' + status); 255 | err.xhr = xhr; 256 | errback(err); 257 | } else { 258 | callback(xhr.responseText); 259 | } 260 | } 261 | }; 262 | xhr.send(null); 263 | }; 264 | } else if (typeof Packages !== 'undefined') { 265 | //Why Java, why is this so awkward? 266 | text.get = function (url, callback) { 267 | var encoding = "utf-8", 268 | file = new java.io.File(url), 269 | lineSeparator = java.lang.System.getProperty("line.separator"), 270 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 271 | stringBuffer, line, 272 | content = ''; 273 | try { 274 | stringBuffer = new java.lang.StringBuffer(); 275 | line = input.readLine(); 276 | 277 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 278 | // http://www.unicode.org/faq/utf_bom.html 279 | 280 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 281 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 282 | if (line && line.length() && line.charAt(0) === 0xfeff) { 283 | // Eat the BOM, since we've already found the encoding on this file, 284 | // and we plan to concatenating this buffer with others; the BOM should 285 | // only appear at the top of a file. 286 | line = line.substring(1); 287 | } 288 | 289 | stringBuffer.append(line); 290 | 291 | while ((line = input.readLine()) !== null) { 292 | stringBuffer.append(lineSeparator); 293 | stringBuffer.append(line); 294 | } 295 | //Make sure we return a JavaScript string and not a Java string. 296 | content = String(stringBuffer.toString()); //String 297 | } finally { 298 | input.close(); 299 | } 300 | callback(content); 301 | }; 302 | } 303 | 304 | return text; 305 | }); 306 | -------------------------------------------------------------------------------- /Excel/Workbook.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module Excel/Workbook 3 | */ 4 | define([ 5 | 'require', 6 | 'underscore', 7 | './util', 8 | './StyleSheet', 9 | './Worksheet', 10 | './SharedStrings', 11 | './RelationshipManager', 12 | './Paths', 13 | './XMLDOM' 14 | ], 15 | function (require, _, util, StyleSheet, Worksheet, SharedStrings, RelationshipManager, Paths, XMLDOM) { 16 | "use strict"; 17 | var Workbook = function (config) { 18 | this.worksheets = []; 19 | this.tables = []; 20 | this.drawings = []; 21 | this.media = {}; 22 | this.initialize(config); 23 | }; 24 | _.extend(Workbook.prototype, { 25 | 26 | initialize: function () { 27 | this.id = _.uniqueId('Workbook'); 28 | this.styleSheet = new StyleSheet(); 29 | this.sharedStrings = new SharedStrings(); 30 | this.relations = new RelationshipManager(); 31 | this.relations.addRelation(this.styleSheet, 'stylesheet'); 32 | this.relations.addRelation(this.sharedStrings, 'sharedStrings'); 33 | }, 34 | 35 | createWorksheet: function (config) { 36 | config = config || {}; 37 | _.defaults(config, { 38 | name: 'Sheet '.concat(this.worksheets.length + 1) 39 | }); 40 | return new Worksheet(config); 41 | }, 42 | 43 | getStyleSheet: function () { 44 | return this.styleSheet; 45 | }, 46 | 47 | addTable: function (table) { 48 | this.tables.push(table); 49 | }, 50 | 51 | addDrawings: function (drawings) { 52 | this.drawings.push(drawings); 53 | }, 54 | 55 | addMedia: function (type, fileName, fileData, contentType) { 56 | var fileNamePieces = fileName.split('.'); 57 | var extension = fileNamePieces[fileNamePieces.length - 1]; 58 | if(!contentType) { 59 | switch(extension.toLowerCase()) { 60 | case 'jpeg': 61 | case 'jpg': 62 | contentType = "image/jpeg"; 63 | break; 64 | case 'png': 65 | contentType = "image/png"; 66 | break; 67 | case 'gif': 68 | contentType = "image/gif"; 69 | break; 70 | default: 71 | contentType = null; 72 | break; 73 | } 74 | } 75 | if(!this.media[fileName]) { 76 | this.media[fileName] = { 77 | id: fileName, 78 | data: fileData, 79 | fileName: fileName, 80 | contentType: contentType, 81 | extension: extension 82 | }; 83 | } 84 | return this.media[fileName]; 85 | }, 86 | 87 | addWorksheet: function (worksheet) { 88 | this.relations.addRelation(worksheet, 'worksheet'); 89 | worksheet.setSharedStringCollection(this.sharedStrings); 90 | this.worksheets.push(worksheet); 91 | }, 92 | 93 | createContentTypes: function () { 94 | var doc = util.createXmlDoc(util.schemas.contentTypes, 'Types'); 95 | var types = doc.documentElement; 96 | var i, l; 97 | 98 | types.appendChild(util.createElement(doc, 'Default', [ 99 | ['Extension', "rels"], 100 | ['ContentType', "application/vnd.openxmlformats-package.relationships+xml"] 101 | ])); 102 | types.appendChild(util.createElement(doc, 'Default', [ 103 | ['Extension', "xml"], 104 | ['ContentType', "application/xml"] 105 | ])); 106 | 107 | var extensions = {}; 108 | for(var filename in this.media) { 109 | if(this.media.hasOwnProperty(filename)) { 110 | extensions[this.media[filename].extension] = this.media[filename].contentType; 111 | } 112 | } 113 | for(var extension in extensions) { 114 | if(extensions.hasOwnProperty(extension)) { 115 | types.appendChild(util.createElement(doc, 'Default', [ 116 | ['Extension', extension], 117 | ['ContentType', extensions[extension]] 118 | ])); 119 | } 120 | } 121 | 122 | types.appendChild(util.createElement(doc, 'Override', [ 123 | ['PartName', "/xl/workbook.xml"], 124 | ['ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"] 125 | ])); 126 | types.appendChild(util.createElement(doc, 'Override', [ 127 | ['PartName', "/xl/sharedStrings.xml"], 128 | ['ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"] 129 | ])); 130 | types.appendChild(util.createElement(doc, 'Override', [ 131 | ['PartName', "/xl/styles.xml"], 132 | ['ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"] 133 | ])); 134 | 135 | for(i = 0, l = this.worksheets.length; i < l; i++) { 136 | types.appendChild(util.createElement(doc, 'Override', [ 137 | ['PartName', "/xl/worksheets/sheet" + (i + 1) + ".xml"], 138 | ['ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"] 139 | ])); 140 | } 141 | for(i = 0, l = this.tables.length; i < l; i++) { 142 | types.appendChild(util.createElement(doc, 'Override', [ 143 | ['PartName', "/xl/tables/table" + (i + 1) + ".xml"], 144 | ['ContentType', "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"] 145 | ])); 146 | } 147 | 148 | for(i = 0, l = this.drawings.length; i < l; i++) { 149 | types.appendChild(util.createElement(doc, 'Override', [ 150 | ['PartName', '/xl/drawings/drawing' + (i + 1) + '.xml'], 151 | ['ContentType', 'application/vnd.openxmlformats-officedocument.drawing+xml'] 152 | ])); 153 | } 154 | 155 | return doc; 156 | }, 157 | 158 | toXML: function () { 159 | var doc = util.createXmlDoc(util.schemas.spreadsheetml, 'workbook'); 160 | var wb = doc.documentElement; 161 | wb.setAttribute('xmlns:r', util.schemas.relationships); 162 | 163 | var maxWorksheetNameLength = 31; 164 | var sheets = util.createElement(doc, 'sheets'); 165 | for(var i = 0, l = this.worksheets.length; i < l; i++) { 166 | var sheet = doc.createElement('sheet'); 167 | // Microsoft Excel (2007, 2013) do not allow worksheet names longer than 31 characters 168 | // if the worksheet name is longer, Excel displays an "Excel found unreadable content..." popup when opening the file 169 | if(console != null && this.worksheets[i].name.length > maxWorksheetNameLength) { 170 | console.log('Microsoft Excel requires work sheet names to be less than ' + (maxWorksheetNameLength+1) + 171 | ' characters long, work sheet name "' + this.worksheets[i].name + 172 | '" is ' + this.worksheets[i].name.length + ' characters long'); 173 | } 174 | sheet.setAttribute('name', this.worksheets[i].name); 175 | sheet.setAttribute('sheetId', i + 1); 176 | sheet.setAttribute('r:id', this.relations.getRelationshipId(this.worksheets[i])); 177 | sheets.appendChild(sheet); 178 | } 179 | wb.appendChild(sheets); 180 | return doc; 181 | }, 182 | 183 | createWorkbookRelationship: function () { 184 | var doc = util.createXmlDoc(util.schemas.relationshipPackage, 'Relationships'); 185 | var relationships = doc.documentElement; 186 | relationships.appendChild(util.createElement(doc, 'Relationship', [ 187 | ['Id', 'rId1'], 188 | ['Type', util.schemas.officeDocument], 189 | ['Target', 'xl/workbook.xml'] 190 | ])); 191 | return doc; 192 | }, 193 | 194 | _generateCorePaths: function (files) { 195 | var i, l; 196 | Paths[this.styleSheet.id] = 'styles.xml'; 197 | Paths[this.sharedStrings.id] = 'sharedStrings.xml'; 198 | Paths[this.id] = '/xl/workbook.xml'; 199 | 200 | for(i = 0, l = this.tables.length; i < l; i++) { 201 | files['/xl/tables/table' + (i + 1) + '.xml'] = this.tables[i].toXML(); 202 | Paths[this.tables[i].id] = '/xl/tables/table' + (i + 1) + '.xml'; 203 | } 204 | 205 | for(var fileName in this.media) { 206 | if(this.media.hasOwnProperty(fileName)) { 207 | var media = this.media[fileName]; 208 | files['/xl/media/' + fileName] = media.data; 209 | Paths[fileName] = '/xl/media/' + fileName; 210 | } 211 | } 212 | 213 | for(i = 0, l = this.drawings.length; i < l; i++) { 214 | files['/xl/drawings/drawing' + (i + 1) + '.xml'] = this.drawings[i].toXML(); 215 | Paths[this.drawings[i].id] = '/xl/drawings/drawing' + (i + 1) + '.xml'; 216 | files['/xl/drawings/_rels/drawing' + (i + 1) + '.xml.rels'] = this.drawings[i].relations.toXML(); 217 | } 218 | 219 | 220 | }, 221 | 222 | _prepareFilesForPackaging: function (files) { 223 | 224 | _.extend(files, { 225 | '/[Content_Types].xml': this.createContentTypes(), 226 | '/_rels/.rels': this.createWorkbookRelationship(), 227 | '/xl/styles.xml': this.styleSheet.toXML(), 228 | '/xl/workbook.xml': this.toXML(), 229 | '/xl/sharedStrings.xml': this.sharedStrings.toXML(), 230 | '/xl/_rels/workbook.xml.rels': this.relations.toXML() 231 | }); 232 | 233 | _.each(files, function (value, key) { 234 | if(key.indexOf('.xml') !== -1 || key.indexOf('.rels') !== -1) { 235 | if (value instanceof XMLDOM){ 236 | files[key] = value.toString(); 237 | } else { 238 | files[key] = value.xml || new window.XMLSerializer().serializeToString(value); 239 | } 240 | var content = files[key].replace(/xmlns=""/g, ''); 241 | content = content.replace(/NS[\d]+:/g, ''); 242 | content = content.replace(/xmlns:NS[\d]+=""/g, ''); 243 | files[key] = '' + "\n" + content; 244 | } 245 | }); 246 | }, 247 | 248 | generateFilesAsync: function (options) { 249 | var requireJsPath = options.requireJsPath; 250 | var self = this; 251 | if(!options.requireJsPath) { 252 | requireJsPath = document.getElementById('requirejs') ? document.getElementById('requirejs').src : ''; 253 | } 254 | if(!requireJsPath) { 255 | throw "Please add 'requirejs' to the script that includes requirejs, or specify the path as an argument"; 256 | } 257 | var files = {}, 258 | doneCount = this.worksheets.length, 259 | stringsCollectedCount = this.worksheets.length, 260 | workers = []; 261 | 262 | var result = { 263 | status: "Not Started", 264 | terminate: function () { 265 | for(var i = 0; i < workers.length; i++) { 266 | workers[i].terminate(); 267 | } 268 | } 269 | }; 270 | this._generateCorePaths(files); 271 | 272 | var done = function () { 273 | if(--doneCount === 0) { 274 | self._prepareFilesForPackaging(files); 275 | for(var i = 0; i < workers.length; i++) { 276 | workers[i].terminate(); 277 | } 278 | options.success(files); 279 | } 280 | }; 281 | var stringsCollected = function () { 282 | if(--stringsCollectedCount === 0) { 283 | for(var i = 0; i < workers.length; i++) { 284 | workers[i].postMessage({ 285 | instruction: 'export', 286 | sharedStrings: self.sharedStrings.exportData() 287 | }); 288 | } 289 | } 290 | }; 291 | 292 | 293 | var worksheetWorker = function (worksheetIndex) { 294 | return { 295 | error: function () { 296 | for(var i = 0; i < workers.length; i++) { 297 | workers[i].terminate(); 298 | } 299 | //message, filename, lineno 300 | options.error.apply(this, arguments); 301 | }, 302 | stringsCollected: function () { 303 | stringsCollected(); 304 | }, 305 | finished: function (data) { 306 | files['/xl/worksheets/sheet' + (worksheetIndex + 1) + '.xml'] = {xml: data}; 307 | Paths[self.worksheets[worksheetIndex].id] = 'worksheets/sheet' + (worksheetIndex + 1) + '.xml'; 308 | files['/xl/worksheets/_rels/sheet' + (worksheetIndex + 1) + '.xml.rels'] = self.worksheets[worksheetIndex].relations.toXML(); 309 | done(); 310 | } 311 | }; 312 | }; 313 | 314 | for(var i = 0, l = this.worksheets.length; i < l; i++) { 315 | workers.push( 316 | this._createWorker(requireJsPath, i, worksheetWorker(i)) 317 | ); 318 | } 319 | 320 | return result; 321 | }, 322 | 323 | _createWorker: function (requireJsPath, worksheetIndex, callbacks) { 324 | var worker = new window.Worker(require.toUrl('./WorksheetExportWorker.js')); 325 | var self = this; 326 | worker.addEventListener('error', callbacks.error); 327 | worker.addEventListener('message', function(event) { 328 | // console.log("Called back by the worker!\n", event.data); 329 | switch(event.data.status) { 330 | case "ready": 331 | worker.postMessage({ 332 | instruction: 'start', 333 | data: self.worksheets[worksheetIndex].exportData() 334 | }); 335 | break; 336 | case "sharedStrings": 337 | for(var i = 0; i < event.data.data.length; i++) { 338 | self.sharedStrings.addString(event.data.data[i]); 339 | } 340 | callbacks.stringsCollected(); 341 | break; 342 | case "finished": 343 | callbacks.finished(event.data.data); 344 | break; 345 | } 346 | }, false); 347 | worker.postMessage({ 348 | instruction: 'setup', 349 | config: { 350 | paths: { 351 | underscore: require.toUrl('underscore').slice(0, -3) 352 | }, 353 | shim: { 354 | 'underscore': { 355 | exports: '_' 356 | } 357 | } 358 | }, 359 | requireJsPath: requireJsPath 360 | }); 361 | return worker; 362 | }, 363 | 364 | generateFiles: function () { 365 | var files = {}; 366 | this._generateCorePaths(files); 367 | 368 | 369 | for(var i = 0, l = this.worksheets.length; i < l; i++) { 370 | files['/xl/worksheets/sheet' + (i + 1) + '.xml'] = this.worksheets[i].toXML(); 371 | Paths[this.worksheets[i].id] = 'worksheets/sheet' + (i + 1) + '.xml'; 372 | files['/xl/worksheets/_rels/sheet' + (i + 1) + '.xml.rels'] = this.worksheets[i].relations.toXML(); 373 | } 374 | 375 | this._prepareFilesForPackaging(files); 376 | 377 | return files; 378 | } 379 | }); 380 | return Workbook; 381 | }); -------------------------------------------------------------------------------- /demo/jasmine-core/json2.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://www.JSON.org/json2.js 3 | 2009-08-17 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | This file creates a global JSON object containing two methods: stringify 12 | and parse. 13 | 14 | JSON.stringify(value, replacer, space) 15 | value any JavaScript value, usually an object or array. 16 | 17 | replacer an optional parameter that determines how object 18 | values are stringified for objects. It can be a 19 | function or an array of strings. 20 | 21 | space an optional parameter that specifies the indentation 22 | of nested structures. If it is omitted, the text will 23 | be packed without extra whitespace. If it is a number, 24 | it will specify the number of spaces to indent at each 25 | level. If it is a string (such as '\t' or ' '), 26 | it contains the characters used to indent at each level. 27 | 28 | This method produces a JSON text from a JavaScript value. 29 | 30 | When an object value is found, if the object contains a toJSON 31 | method, its toJSON method will be called and the result will be 32 | stringified. A toJSON method does not serialize: it returns the 33 | value represented by the name/value pair that should be serialized, 34 | or undefined if nothing should be serialized. The toJSON method 35 | will be passed the key associated with the value, and this will be 36 | bound to the value 37 | 38 | For example, this would serialize Dates as ISO strings. 39 | 40 | Date.prototype.toJSON = function (key) { 41 | function f(n) { 42 | // Format integers to have at least two digits. 43 | return n < 10 ? '0' + n : n; 44 | } 45 | 46 | return this.getUTCFullYear() + '-' + 47 | f(this.getUTCMonth() + 1) + '-' + 48 | f(this.getUTCDate()) + 'T' + 49 | f(this.getUTCHours()) + ':' + 50 | f(this.getUTCMinutes()) + ':' + 51 | f(this.getUTCSeconds()) + 'Z'; 52 | }; 53 | 54 | You can provide an optional replacer method. It will be passed the 55 | key and value of each member, with this bound to the containing 56 | object. The value that is returned from your method will be 57 | serialized. If your method returns undefined, then the member will 58 | be excluded from the serialization. 59 | 60 | If the replacer parameter is an array of strings, then it will be 61 | used to select the members to be serialized. It filters the results 62 | such that only members with keys listed in the replacer array are 63 | stringified. 64 | 65 | Values that do not have JSON representations, such as undefined or 66 | functions, will not be serialized. Such values in objects will be 67 | dropped; in arrays they will be replaced with null. You can use 68 | a replacer function to replace those with JSON values. 69 | JSON.stringify(undefined) returns undefined. 70 | 71 | The optional space parameter produces a stringification of the 72 | value that is filled with line breaks and indentation to make it 73 | easier to read. 74 | 75 | If the space parameter is a non-empty string, then that string will 76 | be used for indentation. If the space parameter is a number, then 77 | the indentation will be that many spaces. 78 | 79 | Example: 80 | 81 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 82 | // text is '["e",{"pluribus":"unum"}]' 83 | 84 | 85 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 86 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 87 | 88 | text = JSON.stringify([new Date()], function (key, value) { 89 | return this[key] instanceof Date ? 90 | 'Date(' + this[key] + ')' : value; 91 | }); 92 | // text is '["Date(---current time---)"]' 93 | 94 | 95 | JSON.parse(text, reviver) 96 | This method parses a JSON text to produce an object or array. 97 | It can throw a SyntaxError exception. 98 | 99 | The optional reviver parameter is a function that can filter and 100 | transform the results. It receives each of the keys and values, 101 | and its return value is used instead of the original value. 102 | If it returns what it received, then the structure is not modified. 103 | If it returns undefined then the member is deleted. 104 | 105 | Example: 106 | 107 | // Parse the text. Values that look like ISO date strings will 108 | // be converted to Date objects. 109 | 110 | myData = JSON.parse(text, function (key, value) { 111 | var a; 112 | if (typeof value === 'string') { 113 | a = 114 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 115 | if (a) { 116 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 117 | +a[5], +a[6])); 118 | } 119 | } 120 | return value; 121 | }); 122 | 123 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 124 | var d; 125 | if (typeof value === 'string' && 126 | value.slice(0, 5) === 'Date(' && 127 | value.slice(-1) === ')') { 128 | d = new Date(value.slice(5, -1)); 129 | if (d) { 130 | return d; 131 | } 132 | } 133 | return value; 134 | }); 135 | 136 | 137 | This is a reference implementation. You are free to copy, modify, or 138 | redistribute. 139 | 140 | This code should be minified before deployment. 141 | See http://javascript.crockford.com/jsmin.html 142 | 143 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 144 | NOT CONTROL. 145 | */ 146 | 147 | /*jslint evil: true */ 148 | 149 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 150 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 151 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 152 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 153 | test, toJSON, toString, valueOf 154 | */ 155 | 156 | "use strict"; 157 | 158 | // Create a JSON object only if one does not already exist. We create the 159 | // methods in a closure to avoid creating global variables. 160 | 161 | if (!this.JSON) { 162 | this.JSON = {}; 163 | } 164 | 165 | (function () { 166 | 167 | function f(n) { 168 | // Format integers to have at least two digits. 169 | return n < 10 ? '0' + n : n; 170 | } 171 | 172 | if (typeof Date.prototype.toJSON !== 'function') { 173 | 174 | Date.prototype.toJSON = function (key) { 175 | 176 | return isFinite(this.valueOf()) ? 177 | this.getUTCFullYear() + '-' + 178 | f(this.getUTCMonth() + 1) + '-' + 179 | f(this.getUTCDate()) + 'T' + 180 | f(this.getUTCHours()) + ':' + 181 | f(this.getUTCMinutes()) + ':' + 182 | f(this.getUTCSeconds()) + 'Z' : null; 183 | }; 184 | 185 | String.prototype.toJSON = 186 | Number.prototype.toJSON = 187 | Boolean.prototype.toJSON = function (key) { 188 | return this.valueOf(); 189 | }; 190 | } 191 | 192 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 193 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 194 | gap, 195 | indent, 196 | meta = { // table of character substitutions 197 | '\b': '\\b', 198 | '\t': '\\t', 199 | '\n': '\\n', 200 | '\f': '\\f', 201 | '\r': '\\r', 202 | '"' : '\\"', 203 | '\\': '\\\\' 204 | }, 205 | rep; 206 | 207 | 208 | function quote(string) { 209 | 210 | // If the string contains no control characters, no quote characters, and no 211 | // backslash characters, then we can safely slap some quotes around it. 212 | // Otherwise we must also replace the offending characters with safe escape 213 | // sequences. 214 | 215 | escapable.lastIndex = 0; 216 | return escapable.test(string) ? 217 | '"' + string.replace(escapable, function (a) { 218 | var c = meta[a]; 219 | return typeof c === 'string' ? c : 220 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 221 | }) + '"' : 222 | '"' + string + '"'; 223 | } 224 | 225 | 226 | function str(key, holder) { 227 | // Produce a string from holder[key]. 228 | 229 | var i, // The loop counter. 230 | k, // The member key. 231 | v, // The member value. 232 | length, 233 | mind = gap, 234 | partial, 235 | value = holder[key]; 236 | 237 | // If the value has a toJSON method, call it to obtain a replacement value. 238 | 239 | if (value && typeof value === 'object' && 240 | typeof value.toJSON === 'function') { 241 | value = value.toJSON(key); 242 | } 243 | 244 | // If we were called with a replacer function, then call the replacer to 245 | // obtain a replacement value. 246 | 247 | if (typeof rep === 'function') { 248 | value = rep.call(holder, key, value); 249 | } 250 | 251 | // What happens next depends on the value's type. 252 | 253 | switch (typeof value) { 254 | case 'string': 255 | return quote(value); 256 | 257 | case 'number': 258 | 259 | // JSON numbers must be finite. Encode non-finite numbers as null. 260 | 261 | return isFinite(value) ? String(value) : 'null'; 262 | 263 | case 'boolean': 264 | case 'null': 265 | 266 | // If the value is a boolean or null, convert it to a string. Note: 267 | // typeof null does not produce 'null'. The case is included here in 268 | // the remote chance that this gets fixed someday. 269 | 270 | return String(value); 271 | 272 | // If the type is 'object', we might be dealing with an object or an array or 273 | // null. 274 | 275 | case 'object': 276 | 277 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 278 | // so watch out for that case. 279 | 280 | if (!value) { 281 | return 'null'; 282 | } 283 | 284 | // Make an array to hold the partial results of stringifying this object value. 285 | 286 | gap += indent; 287 | partial = []; 288 | 289 | // Is the value an array? 290 | 291 | if (Object.prototype.toString.apply(value) === '[object Array]') { 292 | 293 | // The value is an array. Stringify every element. Use null as a placeholder 294 | // for non-JSON values. 295 | 296 | length = value.length; 297 | for (i = 0; i < length; i += 1) { 298 | partial[i] = str(i, value) || 'null'; 299 | } 300 | 301 | // Join all of the elements together, separated with commas, and wrap them in 302 | // brackets. 303 | 304 | v = partial.length === 0 ? '[]' : 305 | gap ? '[\n' + gap + 306 | partial.join(',\n' + gap) + '\n' + 307 | mind + ']' : 308 | '[' + partial.join(',') + ']'; 309 | gap = mind; 310 | return v; 311 | } 312 | 313 | // If the replacer is an array, use it to select the members to be stringified. 314 | 315 | if (rep && typeof rep === 'object') { 316 | length = rep.length; 317 | for (i = 0; i < length; i += 1) { 318 | k = rep[i]; 319 | if (typeof k === 'string') { 320 | v = str(k, value); 321 | if (v) { 322 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 323 | } 324 | } 325 | } 326 | } else { 327 | 328 | // Otherwise, iterate through all of the keys in the object. 329 | 330 | for (k in value) { 331 | if (Object.hasOwnProperty.call(value, k)) { 332 | v = str(k, value); 333 | if (v) { 334 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 335 | } 336 | } 337 | } 338 | } 339 | 340 | // Join all of the member texts together, separated with commas, 341 | // and wrap them in braces. 342 | 343 | v = partial.length === 0 ? '{}' : 344 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 345 | mind + '}' : '{' + partial.join(',') + '}'; 346 | gap = mind; 347 | return v; 348 | } 349 | } 350 | 351 | // If the JSON object does not yet have a stringify method, give it one. 352 | 353 | if (typeof JSON.stringify !== 'function') { 354 | JSON.stringify = function (value, replacer, space) { 355 | // The stringify method takes a value and an optional replacer, and an optional 356 | // space parameter, and returns a JSON text. The replacer can be a function 357 | // that can replace values, or an array of strings that will select the keys. 358 | // A default replacer method can be provided. Use of the space parameter can 359 | // produce text that is more easily readable. 360 | 361 | var i; 362 | gap = ''; 363 | indent = ''; 364 | 365 | // If the space parameter is a number, make an indent string containing that 366 | // many spaces. 367 | 368 | if (typeof space === 'number') { 369 | for (i = 0; i < space; i += 1) { 370 | indent += ' '; 371 | } 372 | 373 | // If the space parameter is a string, it will be used as the indent string. 374 | 375 | } else if (typeof space === 'string') { 376 | indent = space; 377 | } 378 | 379 | // If there is a replacer, it must be a function or an array. 380 | // Otherwise, throw an error. 381 | 382 | rep = replacer; 383 | if (replacer && typeof replacer !== 'function' && 384 | (typeof replacer !== 'object' || 385 | typeof replacer.length !== 'number')) { 386 | throw new Error('JSON.stringify'); 387 | } 388 | 389 | // Make a fake root object containing our value under the key of ''. 390 | // Return the result of stringifying the value. 391 | 392 | return str('', {'': value}); 393 | }; 394 | } 395 | 396 | 397 | // If the JSON object does not yet have a parse method, give it one. 398 | 399 | if (typeof JSON.parse !== 'function') { 400 | JSON.parse = function (text, reviver) { 401 | 402 | // The parse method takes a text and an optional reviver function, and returns 403 | // a JavaScript value if the text is a valid JSON text. 404 | 405 | var j; 406 | 407 | function walk(holder, key) { 408 | 409 | // The walk method is used to recursively walk the resulting structure so 410 | // that modifications can be made. 411 | 412 | var k, v, value = holder[key]; 413 | if (value && typeof value === 'object') { 414 | for (k in value) { 415 | if (Object.hasOwnProperty.call(value, k)) { 416 | v = walk(value, k); 417 | if (v !== undefined) { 418 | value[k] = v; 419 | } else { 420 | delete value[k]; 421 | } 422 | } 423 | } 424 | } 425 | return reviver.call(holder, key, value); 426 | } 427 | 428 | 429 | // Parsing happens in four stages. In the first stage, we replace certain 430 | // Unicode characters with escape sequences. JavaScript handles many characters 431 | // incorrectly, either silently deleting them, or treating them as line endings. 432 | 433 | cx.lastIndex = 0; 434 | if (cx.test(text)) { 435 | text = text.replace(cx, function (a) { 436 | return '\\u' + 437 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 438 | }); 439 | } 440 | 441 | // In the second stage, we run the text against regular expressions that look 442 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 443 | // because they can cause invocation, and '=' because it can cause mutation. 444 | // But just to be safe, we want to reject all unexpected forms. 445 | 446 | // We split the second stage into 4 regexp operations in order to work around 447 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 448 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 449 | // replace all simple value tokens with ']' characters. Third, we delete all 450 | // open brackets that follow a colon or comma or that begin the text. Finally, 451 | // we look to see that the remaining characters are only whitespace or ']' or 452 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 453 | 454 | if (/^[\],:{}\s]*$/. 455 | test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 456 | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 457 | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 458 | 459 | // In the third stage we use the eval function to compile the text into a 460 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 461 | // in JavaScript: it can begin a block or an object literal. We wrap the text 462 | // in parens to eliminate the ambiguity. 463 | 464 | j = eval('(' + text + ')'); 465 | 466 | // In the optional fourth stage, we recursively walk the new structure, passing 467 | // each name/value pair to a reviver function for possible transformation. 468 | 469 | return typeof reviver === 'function' ? 470 | walk({'': j}, '') : j; 471 | } 472 | 473 | // If the text is not JSON parseable, then a SyntaxError is thrown. 474 | 475 | throw new SyntaxError('JSON.parse'); 476 | }; 477 | } 478 | }()); 479 | -------------------------------------------------------------------------------- /Excel/Worksheet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module represents an excel worksheet in its basic form - no tables, charts, etc. Its purpose is 3 | * to hold data, the data's link to how it should be styled, and any links to other outside resources. 4 | * 5 | * @module Excel/Worksheet 6 | */ 7 | define(['underscore', './util', './RelationshipManager'], function (_, util, RelationshipManager) { 8 | "use strict"; 9 | /** 10 | * @constructor 11 | */ 12 | var Worksheet = function (config) { 13 | this.relations = null; 14 | this.columnFormats = []; 15 | this.data = []; 16 | this.mergedCells = []; 17 | this.columns = []; 18 | this._headers = []; 19 | this._footers = []; 20 | this._tables = []; 21 | this._drawings = []; 22 | this._rowInstructions = {}; 23 | this.initialize(config); 24 | }; 25 | _.extend(Worksheet.prototype, { 26 | 27 | initialize: function (config) { 28 | config = config || {}; 29 | this.name = config.name; 30 | this.id = _.uniqueId('Worksheet'); 31 | this._timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000; 32 | if(config.columns) { 33 | this.setColumns(config.columns); 34 | } 35 | 36 | this.relations = new RelationshipManager(); 37 | }, 38 | 39 | /** 40 | * Returns an object that can be consumed by a WorksheetExportWorker 41 | * @returns {Object} 42 | */ 43 | exportData: function () { 44 | return { 45 | relations: this.relations.exportData(), 46 | columnFormats: this.columnFormats, 47 | data: this.data, 48 | columns: this.columns, 49 | mergedCells: this.mergedCells, 50 | _headers: this._headers, 51 | _footers: this._footers, 52 | _tables: this._tables, 53 | _rowInstructions: this._rowInstructions, 54 | name: this.name, 55 | id: this.id 56 | }; 57 | }, 58 | 59 | /** 60 | * Imports data - to be used while inside of a WorksheetExportWorker. 61 | * @param {Object} data 62 | */ 63 | importData: function (data) { 64 | this.relations.importData(data.relations); 65 | delete data.relations; 66 | _.extend(this, data); 67 | }, 68 | 69 | setSharedStringCollection: function (stringCollection) { 70 | this.sharedStrings = stringCollection; 71 | }, 72 | 73 | addTable: function (table) { 74 | this._tables.push(table); 75 | this.relations.addRelation(table, 'table'); 76 | }, 77 | 78 | addDrawings: function (table) { 79 | this._drawings.push(table); 80 | this.relations.addRelation(table, 'drawingRelationship'); 81 | }, 82 | 83 | setRowInstructions: function (rowIndex, instructions) { 84 | this._rowInstructions[rowIndex] = instructions; 85 | }, 86 | 87 | /** 88 | * Expects an array length of three. 89 | * 90 | * @see Excel/Worksheet compilePageDetailPiece 91 | * @see Adding headers and footers to a worksheet 92 | * 93 | * @param {Array} headers [left, center, right] 94 | */ 95 | setHeader: function (headers) { 96 | if(!_.isArray(headers)) { 97 | throw "Invalid argument type - setHeader expects an array of three instructions"; 98 | } 99 | this._headers = headers; 100 | }, 101 | 102 | /** 103 | * Expects an array length of three. 104 | * 105 | * @see Excel/Worksheet compilePageDetailPiece 106 | * @see Adding headers and footers to a worksheet 107 | * 108 | * @param {Array} footers [left, center, right] 109 | */ 110 | setFooter: function (footers) { 111 | if(!_.isArray(footers)) { 112 | throw "Invalid argument type - setFooter expects an array of three instructions"; 113 | } 114 | this._footers = footers; 115 | }, 116 | 117 | /** 118 | * Turns page header/footer details into the proper format for Excel. 119 | * @param {type} data 120 | * @returns {String} 121 | */ 122 | compilePageDetailPackage: function (data) { 123 | data = data || ""; 124 | return [ 125 | "&L", this.compilePageDetailPiece(data[0] || ""), 126 | "&C", this.compilePageDetailPiece(data[1] || ""), 127 | "&R", this.compilePageDetailPiece(data[2] || "") 128 | ].join(''); 129 | }, 130 | 131 | /** 132 | * Turns instructions on page header/footer details into something 133 | * usable by Excel. 134 | * 135 | * @param {type} data 136 | * @returns {String|@exp;_@call;reduce} 137 | */ 138 | compilePageDetailPiece: function (data) { 139 | if(_.isString(data)) { 140 | return '&"-,Regular"'.concat(data); 141 | } 142 | if(_.isObject(data) && !_.isArray(data)) { 143 | var string = ""; 144 | if(data.font || data.bold) { 145 | var weighting = data.bold ? "Bold" : "Regular"; 146 | string += '&"' + (data.font || '-'); 147 | string += ',' + weighting + '"'; 148 | } else { 149 | string += '&"-,Regular"'; 150 | } 151 | if(data.underline) { 152 | string += "&U"; 153 | } 154 | if(data.fontSize) { 155 | string += "&"+data.fontSize; 156 | } 157 | string += data.text; 158 | 159 | return string; 160 | } 161 | 162 | if(_.isArray(data)) { 163 | var self = this; 164 | return _.reduce(data, function (m, v) { 165 | return m.concat(self.compilePageDetailPiece(v)); 166 | }, ""); 167 | } 168 | }, 169 | 170 | /** 171 | * Creates the header node. 172 | * 173 | * @todo implement the ability to do even/odd headers 174 | * @param {XML Doc} doc 175 | * @returns {XML Node} 176 | */ 177 | exportHeader: function (doc) { 178 | var oddHeader = doc.createElement('oddHeader'); 179 | oddHeader.appendChild(doc.createTextNode(this.compilePageDetailPackage(this._headers))); 180 | return oddHeader; 181 | }, 182 | 183 | /** 184 | * Creates the footer node. 185 | * 186 | * @todo implement the ability to do even/odd footers 187 | * @param {XML Doc} doc 188 | * @returns {XML Node} 189 | */ 190 | exportFooter: function (doc) { 191 | var oddFooter = doc.createElement('oddFooter'); 192 | oddFooter.appendChild(doc.createTextNode(this.compilePageDetailPackage(this._footers))); 193 | return oddFooter; 194 | }, 195 | 196 | /** 197 | * This creates some nodes ahead of time, which cuts down on generation time due to 198 | * most cell definitions being essentially the same, but having multiple nodes that need 199 | * to be created. Cloning takes less time than creation. 200 | * 201 | * @private 202 | * @param {XML Doc} doc 203 | * @returns {_L8.Anonym$0._buildCache.Anonym$2} 204 | */ 205 | _buildCache: function (doc) { 206 | var numberNode = doc.createElement('c'); 207 | var value = doc.createElement('v'); 208 | value.appendChild(doc.createTextNode("--temp--")); 209 | numberNode.appendChild(value); 210 | 211 | var formulaNode = doc.createElement('c'); 212 | var formulaValue = doc.createElement('f'); 213 | formulaValue.appendChild(doc.createTextNode("--temp--")); 214 | formulaNode.appendChild(formulaValue); 215 | 216 | var stringNode = doc.createElement('c'); 217 | stringNode.setAttribute('t', 's'); 218 | var stringValue = doc.createElement('v'); 219 | stringValue.appendChild(doc.createTextNode("--temp--")); 220 | stringNode.appendChild(stringValue); 221 | 222 | 223 | return { 224 | number: numberNode, 225 | date: numberNode, 226 | string: stringNode, 227 | formula: formulaNode 228 | }; 229 | }, 230 | 231 | /** 232 | * Runs through the XML document and grabs all of the strings that will 233 | * be sent to the 'shared strings' document. 234 | * 235 | * @returns {Array} 236 | */ 237 | collectSharedStrings: function () { 238 | var data = this.data; 239 | var maxX = 0; 240 | var strings = {}; 241 | for(var row = 0, l = data.length; row < l; row++) { 242 | var dataRow = data[row]; 243 | var cellCount = dataRow.length; 244 | maxX = cellCount > maxX ? cellCount : maxX; 245 | for(var c = 0; c < cellCount; c++) { 246 | var cellValue = dataRow[c]; 247 | var metadata = cellValue && cellValue.metadata || {}; 248 | if (cellValue && typeof cellValue === 'object') { 249 | cellValue = cellValue.value; 250 | } 251 | 252 | if(!metadata.type) { 253 | if(typeof cellValue === 'number') { 254 | metadata.type = 'number'; 255 | } 256 | } 257 | if(metadata.type === "text" || !metadata.type) { 258 | if(typeof strings[cellValue] === 'undefined') { 259 | strings[cellValue] = true; 260 | } 261 | } 262 | } 263 | } 264 | return _.keys(strings); 265 | }, 266 | 267 | toXML: function () { 268 | var data = this.data; 269 | var columns = this.columns || []; 270 | var doc = util.createXmlDoc(util.schemas.spreadsheetml, 'worksheet'); 271 | var worksheet = doc.documentElement; 272 | var i, l, row; 273 | worksheet.setAttribute('xmlns:r', util.schemas.relationships); 274 | worksheet.setAttribute('xmlns:mc', util.schemas.markupCompat); 275 | 276 | var maxX = 0; 277 | var sheetData = util.createElement(doc, 'sheetData'); 278 | 279 | var cellCache = this._buildCache(doc); 280 | 281 | for(row = 0, l = data.length; row < l; row++) { 282 | var dataRow = data[row]; 283 | var cellCount = dataRow.length; 284 | maxX = cellCount > maxX ? cellCount : maxX; 285 | var rowNode = doc.createElement('row'); 286 | 287 | for(var c = 0; c < cellCount; c++) { 288 | columns[c] = columns[c] || {}; 289 | var cellValue = dataRow[c]; 290 | var cell, metadata = cellValue && cellValue.metadata || {}; 291 | 292 | if (cellValue && typeof cellValue === 'object') { 293 | cellValue = cellValue.value; 294 | } 295 | 296 | if(!metadata.type) { 297 | if(typeof cellValue === 'number') { 298 | metadata.type = 'number'; 299 | } 300 | } 301 | 302 | switch(metadata.type) { 303 | case "number": 304 | cell = cellCache.number.cloneNode(true); 305 | cell.firstChild.firstChild.nodeValue = cellValue; 306 | break; 307 | case "date": 308 | cell = cellCache.date.cloneNode(true); 309 | cell.firstChild.firstChild.nodeValue = 25569.0 + ((cellValue - this._timezoneOffset) / (60 * 60 * 24 * 1000)); 310 | break; 311 | case "formula": 312 | cell = cellCache.formula.cloneNode(true); 313 | cell.firstChild.firstChild.nodeValue = cellValue; 314 | break; 315 | case "text": 316 | /*falls through*/ 317 | default: 318 | var id; 319 | if(typeof this.sharedStrings.strings[cellValue] !== 'undefined') { 320 | id = this.sharedStrings.strings[cellValue]; 321 | } else { 322 | id = this.sharedStrings.addString(cellValue); 323 | } 324 | cell = cellCache.string.cloneNode(true); 325 | cell.firstChild.firstChild.nodeValue = id; 326 | break; 327 | } 328 | if(metadata.style) { 329 | cell.setAttribute('s', metadata.style); 330 | } 331 | cell.setAttribute('r', util.positionToLetterRef(c + 1, row + 1)); 332 | rowNode.appendChild(cell); 333 | } 334 | rowNode.setAttribute('r', row + 1); 335 | 336 | if (this._rowInstructions[row]) { 337 | var rowInst = this._rowInstructions[row]; 338 | 339 | if (rowInst.height !== undefined) { 340 | rowNode.setAttribute('customHeight', '1'); 341 | rowNode.setAttribute('ht', rowInst.height); 342 | } 343 | 344 | if (rowInst.style !== undefined) { 345 | rowNode.setAttribute('customFormat', '1'); 346 | rowNode.setAttribute('s', rowInst.style); 347 | } 348 | } 349 | 350 | sheetData.appendChild(rowNode); 351 | } 352 | 353 | if(maxX !== 0) { 354 | worksheet.appendChild(util.createElement(doc, 'dimension', [ 355 | ['ref', util.positionToLetterRef(1, 1) + ':' + util.positionToLetterRef(maxX, data.length)] 356 | ])); 357 | } else { 358 | worksheet.appendChild(util.createElement(doc, 'dimension', [ 359 | ['ref', util.positionToLetterRef(1, 1)] 360 | ])); 361 | } 362 | 363 | if(this.columns.length) { 364 | worksheet.appendChild(this.exportColumns(doc)); 365 | } 366 | worksheet.appendChild(sheetData); 367 | 368 | // 'mergeCells' should be written before 'headerFoot' and 'drawing' due to issue 369 | // with Microsoft Excel (2007, 2013) 370 | if (this.mergedCells.length > 0) { 371 | var mergeCells = doc.createElement('mergeCells'); 372 | for (i = 0, l = this.mergedCells.length; i < l; i++) { 373 | var mergeCell = doc.createElement('mergeCell'); 374 | mergeCell.setAttribute('ref', this.mergedCells[i][0] + ':' + this.mergedCells[i][1]); 375 | mergeCells.appendChild(mergeCell); 376 | } 377 | worksheet.appendChild(mergeCells); 378 | } 379 | 380 | this.exportPageSettings(doc, worksheet); 381 | 382 | if(this._headers.length > 0 || this._footers.length > 0) { 383 | var headerFooter = doc.createElement('headerFooter'); 384 | if(this._headers.length > 0) { 385 | headerFooter.appendChild(this.exportHeader(doc)); 386 | } 387 | if(this._footers.length > 0) { 388 | headerFooter.appendChild(this.exportFooter(doc)); 389 | } 390 | worksheet.appendChild(headerFooter); 391 | } 392 | 393 | if(this._tables.length > 0) { 394 | var tables = doc.createElement('tableParts'); 395 | tables.setAttribute('count', this._tables.length); 396 | for(i = 0, l = this._tables.length; i < l; i++) { 397 | var table = doc.createElement('tablePart'); 398 | table.setAttribute('r:id', this.relations.getRelationshipId(this._tables[i])); 399 | tables.appendChild(table); 400 | } 401 | worksheet.appendChild(tables); 402 | } 403 | 404 | // the 'drawing' element should be written last, after 'headerFooter', 'mergeCells', etc. due 405 | // to issue with Microsoft Excel (2007, 2013) 406 | for(i = 0, l = this._drawings.length; i < l; i++) { 407 | var drawing = doc.createElement('drawing'); 408 | drawing.setAttribute('r:id', this.relations.getRelationshipId(this._drawings[i])); 409 | worksheet.appendChild(drawing); 410 | } 411 | return doc; 412 | }, 413 | 414 | /** 415 | * 416 | * @param {XML Doc} doc 417 | * @returns {XML Node} 418 | */ 419 | exportColumns: function (doc) { 420 | var cols = util.createElement(doc, 'cols'); 421 | for(var i = 0, l = this.columns.length; i < l; i++) { 422 | var cd = this.columns[i]; 423 | var col = util.createElement(doc, 'col', [ 424 | ['min', cd.min || i + 1], 425 | ['max', cd.max || i + 1] 426 | ]); 427 | if (cd.hidden) { 428 | col.setAttribute('hidden', 1); 429 | } 430 | if(cd.bestFit) { 431 | col.setAttribute('bestFit', 1); 432 | } 433 | if(cd.customWidth || cd.width) { 434 | col.setAttribute('customWidth', 1); 435 | } 436 | if(cd.width) { 437 | col.setAttribute('width', cd.width); 438 | } else { 439 | col.setAttribute('width', 9.140625); 440 | } 441 | 442 | cols.appendChild(col); 443 | } 444 | return cols; 445 | }, 446 | 447 | /** 448 | * Sets the page settings on a worksheet node. 449 | * 450 | * @param {XML Doc} doc 451 | * @param {XML Node} worksheet 452 | * @returns {undefined} 453 | */ 454 | exportPageSettings: function (doc, worksheet) { 455 | 456 | if(this._orientation) { 457 | worksheet.appendChild(util.createElement(doc, 'pageSetup', [ 458 | ['orientation', this._orientation] 459 | ])); 460 | } 461 | }, 462 | 463 | /** 464 | * http://www.schemacentral.com/sc/ooxml/t-ssml_ST_Orientation.html 465 | * 466 | * Can be one of 'portrait' or 'landscape'. 467 | * 468 | * @param {String} orientation 469 | * @returns {undefined} 470 | */ 471 | setPageOrientation: function (orientation) { 472 | this._orientation = orientation; 473 | }, 474 | 475 | /** 476 | * Expects an array of column definitions. Each column definition needs to have a width assigned to it. 477 | * 478 | * @param {Array} columns 479 | */ 480 | setColumns: function (columns) { 481 | this.columns = columns; 482 | }, 483 | 484 | /** 485 | * Expects an array of data to be translated into cells. 486 | * 487 | * @param {Array} data Two dimensional array - [ [A1, A2], [B1, B2] ] 488 | * @see Adding data to a worksheet 489 | */ 490 | setData: function (data) { 491 | this.data = data; 492 | }, 493 | 494 | /** 495 | * Merge cells in given range 496 | * 497 | * @param cell1 - A1, A2... 498 | * @param cell2 - A2, A3... 499 | */ 500 | mergeCells: function(cell1, cell2) { 501 | this.mergedCells.push([cell1, cell2]); 502 | }, 503 | 504 | /** 505 | * Expects an array containing an object full of column format definitions. 506 | * http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx 507 | * bestFit 508 | * collapsed 509 | * customWidth 510 | * hidden 511 | * max 512 | * min 513 | * outlineLevel 514 | * phonetic 515 | * style 516 | * width 517 | * @param {Array} columnFormats 518 | */ 519 | setColumnFormats: function (columnFormats) { 520 | this.columnFormats = columnFormats; 521 | } 522 | }); 523 | return Worksheet; 524 | }); 525 | --------------------------------------------------------------------------------