├── README.md
├── lib
└── spreadsheets
│ ├── Spreadsheets.js
│ ├── action9.js
│ ├── chain.js
│ ├── core.js
│ └── fork.js
├── package.json
└── xml
├── batchcellentry.xml
├── batchfeed.xml
├── cellentry.xml
├── docentry.xml
└── worksheetentry.xml
/README.md:
--------------------------------------------------------------------------------
1 | node-spreadsheets
2 | =
3 |
4 | An adaptor for Google Spreadsheets API.
5 |
6 | Install
7 | -
8 |
9 | npm install spreadsheets
10 |
11 |
12 | Usage
13 | -
14 |
15 | require `spreadsheets`
16 |
17 | ```js
18 | var authenticate = require("spreadsheets");
19 |
20 | ```
21 |
22 | authenticate with Google Client Login & get your spreadsheets list
23 |
24 | ```js
25 | authenticate({
26 | Email: your email for google account,
27 | Passwd: password
28 |
29 | }, function(err, spreadsheets) {
30 | if(err)
31 | // handle error
32 | spreadsheets.list(function(err, list) {
33 | if(err)
34 | // handle error
35 | ........
36 | });
37 | });
38 | ```
39 |
40 | choose a spreadsheet and a worksheet
41 |
42 | ```js
43 | var spreadsheet = list[ spreadsheet key ];
44 | spreadsheet.worksheet(function(err, sheets) {
45 | if(err))
46 | // handle error
47 | var worksheet = sheets[ *worksheet key* ];
48 | });
49 | ```
50 |
51 | get cells and change value
52 |
53 | ```js
54 | worksheet.cell({
55 | "min-row": 2,
56 | "min-col": 4,
57 | "max-col": 4
58 |
59 | }, function(err, cells) {
60 | if(err)
61 | // handle error
62 | var cell = cells[ cell key ]; // for example R3C4
63 |
64 | cell.changeValue("any value", function(err) {
65 | if(err)
66 | // handle error
67 | ......
68 | });
69 | });
70 | ```
71 |
72 | change styles
73 |
74 | ```js
75 | worksheet.style({
76 | scol: 2,
77 | ecol: 3,
78 | srow: 2,
79 | erow: 4
80 |
81 | }, function(err, style) {
82 | if(err)
83 | // handle error
84 | style.color("#ff0000", function() {});
85 | style.bgColor("#ff0000", function() {});
86 | style.fontsize("10pt", function() {});
87 | style.align("right", function() {});
88 | });
89 | ```
90 |
91 | Dependency
92 | =
93 | * [node-jQuery](https://github.com/praized/node-jquery)
94 |
95 |
--------------------------------------------------------------------------------
/lib/spreadsheets/Spreadsheets.js:
--------------------------------------------------------------------------------
1 | /***/
2 |
3 | var fs = require("fs"), querystring = require('querystring'), url = require("url");
4 | var path = require("path"), $ = require("jquery");
5 | var core = require("./core"), chain = require("./chain"), fork = require("./fork");
6 |
7 | // extend jQuery
8 | (function($) {
9 | $.fn.outerXml = function() {
10 | var xml = this.wrap("").parent().html();
11 | xml = xml.replace(/(]+>)/g, "$1");
12 | return xml;
13 | };
14 | })($);
15 |
16 | module.exports = exports = spreadsheets;
17 |
18 | function spreadsheets(auth, callback) {
19 | if(auth && $.isFunction(callback))
20 | clientLogin.call(this, auth, function(err, _auth) {
21 | var sheet = !err && Spreadsheets(_auth);
22 | callback(err, sheet);
23 | });
24 | }
25 |
26 | function clientLogin(auth, callback) {
27 | var self = this, _auth;
28 |
29 | fork.call(self, function(cb) {
30 | _clientLogin("wise", cb);
31 |
32 | }, function(cb) {
33 | _clientLogin("writely", cb);
34 |
35 | }, function(err, args) {
36 | if(!err) {
37 | _auth = {}, _auth["wise"] = {}, _auth["writely"] = {};
38 | _auth["wise"].Authorization = "GoogleLogin auth=" + args[0].Auth;
39 | _auth["writely"].Authorization = "GoogleLogin auth=" + args[1].Auth;
40 | }
41 | callback.call(self, err, _auth);
42 |
43 | });
44 |
45 | function _clientLogin(service, _callback) {
46 | chain.call(self, function(next) {
47 | var data = querystring.stringify({
48 | accountType: "GOOGLE",
49 | service: service,
50 | Email: auth.Email,
51 | Passwd: auth.Passwd
52 | });
53 | core.authenticate.call(self, null, data, next);
54 |
55 | }, function(info, next) {
56 | var auth = {};
57 | var i, line = info.split(/\n/);
58 | for (i = line.length; i--;)
59 | if(line[i]) {
60 | var kv = line[i].split("=");
61 | auth[kv[0]] = kv[1];
62 | }
63 | // _auth["wise"].Authorization = "GoogleLogin auth=" + auth.Auth;
64 | next.call(self, null, auth);
65 |
66 | }, _callback);
67 | }
68 |
69 | };
70 |
71 | function Spreadsheets(auth) {
72 | var self = this;
73 | if(!(self instanceof Spreadsheets))
74 | return new Spreadsheets(auth);
75 | self._auth = auth, self._list = {};
76 | }
77 |
78 | Spreadsheets.prototype.list = function(callback) {
79 | var self = this, opt = {
80 | headers: self._auth["wise"]
81 | };
82 | chain.call(self, function(next) {
83 | core.list.call(self, opt, "", next);
84 |
85 | }, function(xml, next) {
86 | var $entries = $("entry", xml), list = self._list = {};
87 | $entries.each(function() {
88 | var $this = $(this), $id = $("id", $this);
89 | var sp = $id.text().split(/\//), key = sp[sp.length - 1];
90 | var ss = Spreadsheet(key, self._auth);
91 | ss._xml = $this.outerXml();
92 | list[key] = ss;
93 | });
94 | next.call(null, null, list);
95 |
96 | }, callback);
97 | };
98 |
99 | Spreadsheets.prototype.createSpreadsheet = function(settings, callback) {
100 | if($.isFunction(settings))
101 | callback = settings, settings = {};
102 | var self = this, opt = {
103 | headers: self._auth["writely"]
104 | };
105 |
106 | chain.call(self, function(next) {
107 | var xml = $(fs.readFileSync("xml/docentry.xml").toString());
108 | $("title", xml).text(settings.title || "untitled");
109 | var data = xml.outerXml();
110 | core.createSpreadsheet.call(self, opt, data, next);
111 |
112 | }, function(xml, next) {
113 | var $xml = $(xml);
114 | var id = $("id", $xml).text();
115 | var sp = id.split(/spreadsheet%3A/), key = sp[1];
116 | var ss = Spreadsheet(key, self._auth);
117 | ss._xml = $xml.outerXml();
118 | self._list[key] = ss;
119 |
120 | next.call(self, null, ss);
121 |
122 | }, callback);
123 | };
124 |
125 | Spreadsheets.prototype.downloadSpreadsheet = function(key, format, out, callback) {
126 | var self = this;
127 |
128 | chain.call(self, function(next) {
129 | self.getSpreadsheet(key, next);
130 |
131 | }, function(ss, next) {
132 | ss.download(format, out, next);
133 |
134 | }, callback);
135 | };
136 |
137 | Spreadsheets.prototype.moveToTrash = function(key, callback) {
138 | var self = this;
139 |
140 | chain.call(self, function(next) {
141 | self.getSpreadsheet(key, next);
142 |
143 | }, function(ss, next) {
144 | ss.trash(next);
145 |
146 | }, callback);
147 | };
148 |
149 | Spreadsheets.prototype.deleteSpreadsheet = function(key, callback) {
150 | var self = this;
151 |
152 | chain.call(self, function(next) {
153 | self.getSpreadsheet(key, next);
154 |
155 | }, function(ss, next) {
156 | ss["delete"](next);
157 |
158 | }, callback);
159 | };
160 |
161 | Spreadsheets.prototype.getSpreadsheet = function(key, callback) {
162 | var self = this, list = self._list, opt = {
163 | path: "/feeds/default/private/full/" + key,
164 | headers: self._auth["writely"]
165 | };
166 |
167 | chain.call(self, function(next) {
168 | key in list ? next.call(null, null, list[key]): core.getSpreadsheet.call(self, opt, "", next);
169 |
170 | }, function(xml, next) {
171 | var ss = Spreadsheet(key, self._auth);
172 | ss._xml = xml;
173 | next.call(self, null, ss);
174 |
175 | }, callback);
176 | };
177 |
178 | /**
179 | * Spreadsheet
180 | */
181 | function Spreadsheet(key, auth) {
182 | var self = this;
183 | if(!(self instanceof Spreadsheet))
184 | return new Spreadsheet(key, auth);
185 | self._key = key, self._auth = auth["wise"], self._docauth = auth["writely"];
186 | self._list = {}, self._titles = {};
187 | }
188 |
189 | Spreadsheet.prototype.worksheet = function(callback) {
190 | var self = this, opt = {
191 | path: "/feeds/worksheets/" + self._key + "/private/full",
192 | headers: self._auth
193 | };
194 |
195 | chain.call(self, function(next) {
196 | core.worksheet.call(self, opt, "", next);
197 |
198 | }, function(xml, next) {
199 | var $entries = $("entry", xml);
200 | var list = self._list = {}, titles = self._titles = {};
201 | $entries.each(function() {
202 | var $this = $(this);
203 | var id = $("id", $this), name = $("title", $this).text();
204 | var sp = id.text().split(/\//), key = sp[sp.length - 1];
205 | var ws = Worksheet(key, name, self);
206 | ws._xml = $this.outerXml();
207 | // list[key] = ws;
208 | // titles[name] = ws;
209 | });
210 | next.call(self, null, list);
211 |
212 | }, callback);
213 | };
214 |
215 | Spreadsheet.prototype.addWorksheet = function(settings, callback) {
216 | if($.isFunction(settings))
217 | callback = settings, settings = {};
218 | var self = this, opt = {
219 | path: "/feeds/worksheets/" + self._key + "/private/full",
220 | headers: self._auth
221 | };
222 | var xml = $(fs.readFileSync("xml/worksheetentry.xml").toString());// TODO
223 | // async
224 | $("title", xml).text(settings.title || "untitled");
225 | $("gs\\:rowCount", xml).text(settings.row || 100);
226 | $("gs\\:colCount", xml).text(settings.col || 20);
227 | var data = xml.outerXml();
228 | // TODO Any bug depending on jquery will lurk...
229 | data = data.replace(/count/g, "Count");
230 |
231 | chain.call(self, function(next) {
232 | core.addWorksheet.call(self, opt, data, next);
233 |
234 | }, function(xml, next) {
235 | var $xml = $(xml);
236 | var id = $("id", $xml).text(), name = $("title", $xml).text();
237 | var sp = id.split(/\//), key = sp[sp.length - 1];
238 | var ws = self._list[key] = Worksheet(key, name, self);
239 | ws._xml = $xml.outerXml();
240 | next.call(self, null, ws);
241 |
242 | }, callback);
243 | };
244 |
245 | /**
246 | * format values: [xls, csv, pdf, ods, tsv, html]
247 | */
248 | Spreadsheet.prototype.download = function(format, out, callback) {
249 | if("function" === typeof format)
250 | callback = format, format = "xls", out = ".";
251 |
252 | else if("function" === typeof out)
253 | callback = out, out = ".";
254 |
255 | var self = this;
256 | var $content = $("content", self._xml).eq(0), src = $content.attr("src");
257 | var _url = url.parse(src);
258 |
259 | if("string" === typeof out)
260 | out = _create(out);
261 |
262 | var opt = {
263 | host: _url["hostname"],
264 | path: _url["pathname"] + "?" + _url["query"] + "&exportFormat=" + format,
265 | headers: self._auth,
266 | outputStream: out
267 | };
268 | core.download.call(self, opt, "", callback);
269 |
270 | function _create(pathname) {
271 | if(path.existsSync(pathname))
272 | if(fs.statSync(pathname).isDirectory())
273 | return _create(path.join(pathname, $("title", self._xml).text() + "." + format));
274 | else {
275 | var s, splits = pathname.split(/\./), len = splits.length;
276 | var index = 2 <= len ? len - 2: 0, fname = splits[index];
277 | if(s = fname.match(/\((\d+)\)$/))
278 | splits[index] = fname.replace(/\(\d+\)$/, "(" + (+s[1] + 1) + ")");
279 | else
280 | splits[index] += "(1)";
281 | return _create(splits.join("."));
282 | }
283 | else
284 | return fs.createWriteStream(pathname);
285 | }
286 | };
287 |
288 | Spreadsheet.prototype.trash = function(callback) {
289 | _delete.call(this, false, callback);
290 | };
291 |
292 | Spreadsheet.prototype["delete"] = function(callback) {
293 | _delete.call(this, true, callback);
294 | };
295 |
296 | function _delete(flg, callback) {
297 | var self = this, key = self._key;
298 | var opt = {
299 | path: "/feeds/default/private/full/" + key + "?delete=" + flg,
300 | headers: $.extend({
301 | "If-None-Match": "W/\"" + key + ".\""
302 | }, self._docauth)
303 | };
304 | core.deleteSpreadsheet.call(self, opt, "", callback);
305 | }
306 |
307 | /**
308 | * Worksheet
309 | */
310 | function Worksheet(key, title, spreadsheet) {
311 | var self = this;
312 | if(!(self instanceof Worksheet))
313 | return new Worksheet(key, title, spreadsheet);
314 | self._key = key, self._title = title, self._parent = spreadsheet;
315 | spreadsheet._list[key] = spreadsheet._titles[title] = self;
316 | }
317 |
318 | Worksheet.prototype.row = function(callback) {
319 | var self = this, opt = {
320 | path: "/feeds/list/" + self._parent._key + "/" + self._key
321 | + "/private/full",
322 | headers: self._parent._auth
323 | };
324 |
325 | chain.call(self, function(next) {
326 | core.row.call(self, opt, "", next);
327 |
328 | }, function(xml, next) {
329 | var $entries = $("entry", xml), list = self._list = {};
330 | $entries.each(function() {
331 | var $this = $(this), $id = $("id", $this);
332 | var sp = $id.text().split(/\//), key = sp[sp.length - 1];
333 | var row = Row(key, self);
334 | row._xml = $this.outerXml();
335 | list[key] = row;
336 | });
337 | next.call(self, null, list);
338 |
339 | }, callback);
340 | };
341 |
342 | Worksheet.prototype.cell = function(query, callback) {
343 | if($.isFunction(query))
344 | callback = query, query = "";
345 | if(typeof query !== "string" && !(query instanceof String))
346 | query = querystring.stringify(query);
347 | if(!!query && /^[^\?]/.test(query))
348 | query = "?" + query;
349 | var self = this, opt = {
350 | path: "/feeds/cells/" + self._parent._key + "/" + self._key
351 | + "/private/full" + query,
352 | headers: self._parent._auth
353 | };
354 |
355 | chain.call(self, function(next) {
356 | core.cell.call(self, opt, "", next);
357 |
358 | }, function(xml, next) {
359 | var $entries = $("entry", xml), list = self._list = self._list || {};
360 | $entries.each(function() {
361 | var $this = $(this), $id = $("id", $this);
362 | var name = $("title", $this).text(), val = $("content", $this).text();
363 | var sp = $id.text().split(/\//), key = sp[sp.length - 1];
364 | var cell = Cell(key, name, val, self);
365 | cell._xml = $this.outerXml();
366 | list[key] = cell;
367 | });
368 | next.call(self, null, list);
369 |
370 | }, callback);
371 | };
372 |
373 | Worksheet.prototype.getCell = function(key, callback) {
374 | var self = this;
375 | var cell = Cell(key, null, null, self);
376 | cell._get(callback);
377 | };
378 |
379 | var Style = require("./action9");
380 | Worksheet.prototype.style = function(range, callback) {
381 | var self = this;
382 | callback.call(self, null, Style(range, self));
383 | };
384 |
385 | /**
386 | * vals = [[row, col, inputValue], ...]
387 | */
388 | Worksheet.prototype.updateCells = function(vals, callback) {
389 | var self = this, p = self._parent;
390 | var path = "/feeds/cells/" + p._key + "/" + self._key + "/private/full";
391 | var id = "https://spreadsheets.google.com" + path;
392 |
393 | var $feed;
394 | chain.call(self, function(next) {
395 | fs.readFile("xml/batchfeed.xml", "utf8", next);
396 | }, function(buf, next) {
397 | $feed = $(buf.toString());
398 | $("id", $feed).text(id);
399 | fs.readFile("xml/batchcellentry.xml", "utf8", next);
400 | }, function(buf, next) {
401 | var xml = buf.toString(), opt = {
402 | path: path + "/batch",
403 | headers: $.extend({
404 | "If-None-Match": "W/\"" + p._key + ".\""
405 | }, p._auth)
406 | }, i, newCells = vals;
407 | for (i = newCells.length; i--;) {
408 | var $entry = $(xml), cell = newCells[i];
409 | var _id = id + "/R" + cell[0] + "C" + cell[1];
410 | $("batch\\:id", $entry).text("A" + i);
411 | $("title", $entry).text("A" + i);
412 | $("id", $entry).text(_id);
413 | $("link", $entry).attr({
414 | href: _id + "/1"
415 | });
416 | $("gs\\:cell", $entry).attr({
417 | row: cell[0],
418 | col: cell[1],
419 | inputValue: cell[2]
420 | });
421 | $feed.append($entry);
422 | }
423 | core.updateCells.call(self, opt, $feed.outerXml(), next);
424 | }, function(xml, next) {
425 | var err = /error/i.test(xml) ? new Error(xml): null;
426 | next.call(self, err);
427 |
428 | }, callback);
429 | };
430 |
431 | /**
432 | * TODO Row
433 | */
434 | function Row(key, worksheet) {
435 | var self = this;
436 | if(!(self instanceof Row))
437 | return new Row(key, worksheet);
438 | self._key = key;
439 | self._parent = worksheet;
440 | }
441 |
442 | /**
443 | * Cell
444 | */
445 | function Cell(key, title, val, worksheet) {
446 | var self = this;
447 | if(!(self instanceof Cell))
448 | return new Cell(key, worksheet);
449 | var rc = (new RegExp("^R(\\d+)C(\\d+)$")).exec(key);
450 | self._key = key, self._row = +rc[1], self._col = +rc[2];
451 | self._title = title, self._value = val, self._parent = worksheet;
452 | }
453 |
454 | Cell.prototype.style = function(callback) {
455 | var self = this, range = {};
456 | range.scol = range.ecol = self._col;
457 | range.srow = range.erow = self._row;
458 | self._parent.style(range, callback);
459 | };
460 |
461 | Cell.prototype.changeValue = function(val, callback) {
462 | var self = this, p = self._parent, gp = p._parent;
463 | var path = "/feeds/cells/" + gp._key + "/" + p._key + "/private/full/"
464 | + self._key;
465 |
466 | chain.call(self, function(next) {
467 | fs.readFile("xml/cellentry.xml", "utf8", next);
468 | }, function(buf, next) {
469 | var $xml = $(buf.toString());
470 | var id = "https://spreadsheets.google.com" + path;
471 | $("id", $xml).text(id);
472 | $("link", $xml).attr({
473 | href: id
474 | });
475 | $("gs\\:cell", $xml).attr({
476 | row: self._row,
477 | col: self._col,
478 | inputValue: val
479 | });
480 | var data = $xml.outerXml(), opt = {
481 | path: path,
482 | headers: $.extend({
483 | "If-None-Match": "W/\"" + gp._key + ".\""
484 | }, gp._auth)
485 | };
486 | core.changeCell.call(self, opt, data, next);
487 |
488 | }, function(xml, next) {
489 | self._xml = $(xml).outerXml();
490 | next.call(self, null, self);
491 |
492 | }, callback);
493 | };
494 |
495 | Cell.prototype.getValue = function(callback) {
496 | var self = this, p = self._parent, key = self._key;
497 | chain.call(self, self._get, function(cell, next) {
498 | callback.call(self, null, cell ? cell._value: null);
499 | });
500 | };
501 |
502 | Cell.prototype._get = function(callback) {
503 | var self = this, p = self._parent;
504 | var query = {};
505 | chain.call(self, function(next) {
506 | query["min-row"] = query["max-row"] = self._row;
507 | query["min-col"] = query["max-col"] = self._col;
508 | p.cell(query, next);
509 |
510 | }, function(cells) {
511 | var key = self._key, cell = cells[key] = cells[key]
512 | || Cell(key, null, null, p);
513 | p._list[key] = cell;
514 | callback.call(self, null, cell);
515 | });
516 | };
517 |
--------------------------------------------------------------------------------
/lib/spreadsheets/action9.js:
--------------------------------------------------------------------------------
1 | /***/
2 |
3 | var querystring = require("querystring"), core = require("./core"), chain = require("./chain");
4 |
5 | module.exports = exports = Style;
6 |
7 | function Style(range, worksheet) {
8 | var self = this;
9 | if(!(self instanceof Style))
10 | return new Style(range, worksheet);
11 | self.range(range), self._ws = worksheet, self._id = worksheet._id;
12 | self._gid = worksheet._gid, self._auth = worksheet._parent._auth;
13 | }
14 |
15 | Style.FONT = {
16 | Normal: "arial,sans,sans-serif",
17 | "Normal/serif": "times new roman,serif",
18 | CourierNew: "courier new,monospace",
19 | Georgia: "georgia",
20 | TrebuchetMS: "trebuchet ms",
21 | Verdana: "verdana"
22 | };
23 | Style.TEXT = ["underline", "line-through", "none"];
24 |
25 | var fn, fns = {
26 | format: 8, // TODO
27 | font: 9, // Stryle.FONT
28 | fontsize: 10, // 6pt
29 | bold: 11, // bold or normal
30 | italic: 12, // italic or normal
31 | line: 13, // underline, line-through or none
32 | wrap: 14, // nowrap or normal
33 | align: 15, // left/center/right
34 | valign: 16, // top/middle/bottom
35 | color: 17, // #ff0000
36 | bgColor: 31, // #ffffff
37 | drawLines: 32, // 111111 top/left/bottom/right/holizontal/vertical
38 | fixRow: 40,
39 | rowHeight: 41, // number
40 | columnWidth: 42, // number
41 | joinCells: 43, // null
42 | fixColumn: 45,
43 | insertAbove: 61, // number
44 | insertLeft: 63, // number
45 | insertBelow: 68, // number
46 | insertRight: 69, // number
47 | rangeNames: 91,
48 | comment: 501,
49 | uncomment: 502
50 | };
51 |
52 | for (fn in fns)
53 | Style.prototype[fn] = reflect(fns[fn]);
54 |
55 | function reflect(num) {
56 | return function(val, callback) {
57 | _query.call(this, num, val, callback);
58 | };
59 | }
60 |
61 | Style.prototype.initialize = function(callback) {
62 | var self = this;
63 |
64 | if(self._id && self._gid)
65 | return callback.call(self, null);
66 |
67 | var key = self._ws._parent._key, auth = self._auth, titles = self._ws._parent._titles;
68 |
69 | chain.call(self, function(next) {
70 | var opt = {
71 | path: "/spreadsheet/ccc?&key=" + key,
72 | headers: auth
73 | };
74 | core.ccc.call(self, opt, "", next);
75 |
76 | }, function(info, next) {
77 | var ser = (new RegExp(/*key + */"[\\w\\-]+\\.\\d+\\.\\d+")).exec(info);
78 | self._id = self._ws._id = ser[0];
79 | var i, s = (new RegExp("structure: \\({action:0, a:(\\[[^\\]]*\\])"))
80 | .exec(info)[1];
81 | var sheets = JSON.parse(s);
82 | for (i = sheets.length; i--;)
83 | if(sheets[i]["g"]in titles)
84 | titles[sheets[i]["g"]]._gid = sheets[i]["i"];
85 | self._gid = self._ws._gid;
86 | next.call(self, null);
87 |
88 | }, callback);
89 |
90 | };
91 |
92 | /**
93 | * @deprecated
94 | * @param range
95 | */
96 | Style.prototype.range = function(range) {
97 | this._range = range;
98 | };
99 |
100 | function _query(num, val, callback) {
101 | var self = this;
102 |
103 | var fncs = [self.initialize, function(next) {
104 | var r = self._range;
105 | r.action = 9;
106 | r.atyp = num;
107 | r.gid = self._gid;
108 | r.v = val;
109 | var data = querystring.stringify(r);
110 | var opt = {
111 | path: "/spreadsheet/edit/action9?&id=" + self._id,
112 | headers: self._auth
113 | };
114 | core.action9.call(self, opt, data, next);
115 | }, callback];
116 | chain.apply(self, fncs);
117 | }
118 |
119 | // var ex = {};
120 | //
121 | // ex[8] = function format() {
122 | // // v=%24%23%2C%23%230.00
123 | // // v=0.00%25
124 | // };
125 |
--------------------------------------------------------------------------------
/lib/spreadsheets/chain.js:
--------------------------------------------------------------------------------
1 | /***/
2 |
3 | module.exports = function() {
4 | var self = this;
5 | var actors = Array.prototype.slice.call(arguments);
6 | next();
7 | function next(err) {
8 | try {
9 | if(err)
10 | return actors.pop().call(self, err);
11 | var actor = actors.shift();
12 | var args = Array.prototype.slice.call(arguments);
13 | if(actors.length > 0) {
14 | args = args.slice(1).concat(next);
15 | }
16 | actor.apply(self, args);
17 | } catch (error) {
18 | actors.length === 0 ? actor.call(self, error): next(error);
19 | }
20 | }
21 | };
--------------------------------------------------------------------------------
/lib/spreadsheets/core.js:
--------------------------------------------------------------------------------
1 | /***/
2 | var http = require("http"), https = require("https"), querystring = require("querystring");
3 | var $ = require("jquery");
4 |
5 | var VERSION = "3.0";
6 |
7 | var core = {
8 | _default: {
9 | opt: function(opt) {
10 | return opt;
11 | },
12 | success: 200
13 | },
14 | authenticate: {
15 | opt: function() {
16 | return {
17 | host: "www.google.com",
18 | path: "/accounts/ClientLogin",
19 | method: "POST",
20 | headers: {
21 | "Content-Type": "application/x-www-form-urlencoded"
22 | }
23 | };
24 | },
25 | success: 200
26 | },
27 |
28 | list: {
29 | opt: function(opt) {
30 | return $.extend(true, {
31 | host: "spreadsheets.google.com",
32 | path: "/feeds/spreadsheets/private/full",
33 | method: "GET",
34 | headers: {
35 | "Content-Type": "application/x-www-form-urlencoded",
36 | "GDATA-Version": VERSION
37 | }
38 | }, opt);
39 | },
40 | success: 200
41 | },
42 |
43 | worksheet: {
44 | opt: function(opt) {
45 | return $.extend(true, {
46 | host: "spreadsheets.google.com",
47 | method: "GET",
48 | headers: {
49 | "Content-Type": "application/x-www-form-urlencoded",
50 | "GDATA-Version": VERSION
51 | }
52 | }, opt);
53 | },
54 | success: 200
55 | },
56 |
57 | addWorksheet: {
58 | opt: function(opt) {
59 | return $.extend(true, {
60 | host: "spreadsheets.google.com",
61 | method: "POST",
62 | headers: {
63 | "Content-Type": "application/atom+xml",
64 | "GDATA-Version": VERSION
65 | }
66 | }, opt);
67 | },
68 | success: 201
69 | },
70 |
71 | changeCell: {
72 | opt: function(opt) {
73 | return $.extend(true, {
74 | host: "spreadsheets.google.com",
75 | method: "PUT",
76 | headers: {
77 | "Content-Type": "application/atom+xml",
78 | "GDATA-Version": VERSION
79 | }
80 | }, opt);
81 | },
82 | success: 200
83 | },
84 |
85 | updateCells: {
86 | opt: function(opt) {
87 | return $.extend(true, {
88 | host: "spreadsheets.google.com",
89 | method: "POST",
90 | headers: {
91 | "Content-Type": "application/atom+xml",
92 | "GDATA-Version": VERSION
93 | }
94 | }, opt);
95 | },
96 | success: 200
97 | },
98 |
99 | deleteWorksheet: {
100 | opt: function(opt) {
101 | return $.extend(true, {
102 | hots: "spreadsheets.google.com",
103 | method: "DELETE",
104 | headers: {
105 | "Content-Type": "application/x-www-form-urlencoded",
106 | "GDATA-Version": VERSION
107 | }
108 | }, opt);
109 | },
110 | success: 200
111 | // TODO
112 | },
113 |
114 | cell: {
115 | opt: function(opt) {
116 | return $.extend(true, {
117 | host: "spreadsheets.google.com",
118 | method: "GET",
119 | headers: {
120 | "Content-Type": "application/x-www-form-urlencoded",
121 | "GDATA-Version": VERSION
122 | }
123 | }, opt);
124 | },
125 | success: 200
126 | },
127 |
128 | row: {
129 | opt: function(opt) {
130 | return $.extend(true, {
131 | host: "spreadsheets.google.com",
132 | method: "GET",
133 | headers: {
134 | "Content-Type": "application/x-www-form-urlencoded",
135 | "GDATA-Version": VERSION
136 | }
137 | }, opt);
138 | },
139 | success: 200
140 | },
141 |
142 | ccc: {
143 | opt: function(opt) {
144 | return $.extend(true, {
145 | host: "docs.google.com",
146 | method: "GET",
147 | headers: {
148 | "X-Same-Domain": "trix",
149 | "Content-Type": "application/x-www-form-urlencoded",
150 | // "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
151 | // AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.10
152 | // Safari/535.1",
153 | "User-Agent": "AppleWebKit/535.1"
154 | }
155 | }, opt);
156 | },
157 | success: 200
158 | },
159 |
160 | action9: {
161 | opt: function(opt) {
162 | return $.extend(true, {
163 | host: "docs.google.com",
164 | method: "POST",
165 | headers: {
166 | "X-Same-Domain": "trix",
167 | "Content-Type": "application/x-www-form-urlencoded"
168 | }
169 | }, opt);
170 | },
171 | success: 200
172 | },
173 |
174 | createSpreadsheet: {
175 | opt: function(opt) {
176 | return $.extend(true, {
177 | host: "docs.google.com",
178 | path: "/feeds/default/private/full",
179 | method: "POST",
180 | headers: {
181 | "Content-Type": "application/atom+xml",
182 | "GDATA-Version": VERSION
183 | }
184 | }, opt);
185 | },
186 | success: 201
187 | },
188 |
189 | getSpreadsheet: {
190 | opt: function(opt) {
191 | return $.extend(true, {
192 | host: "docs.google.com",
193 | method: "GET",
194 | headers: {
195 | "Content-Type": "application/atom+xml",
196 | "GDATA-Version": VERSION
197 | }
198 | }, opt);
199 | },
200 | success: 200
201 | },
202 |
203 | download: {
204 | opt: function(opt) {
205 | return $.extend(true, {
206 | method: "GET",
207 | headers: {
208 | "Content-Type": "application/atom+xml",
209 | "GDATA-Version": VERSION
210 | }
211 | }, opt);
212 | },
213 | success: 200
214 | },
215 |
216 | deleteSpreadsheet: {
217 | opt: function(opt) {
218 | return $.extend(true, {
219 | method: "DELETE",
220 | host: "docs.google.com",
221 | headers: {
222 | "Content-Type": "application/atom+xml",
223 | "GDATA-Version": VERSION
224 | }
225 | }, opt);
226 | },
227 | success: 200
228 | }
229 | };
230 |
231 | var fn;
232 | for (fn in core) {
233 | exports[fn] = _fn(core[fn]);
234 | }
235 |
236 | function _fn(settings) {
237 | return function(opt, data, callback) {
238 | var self = this;
239 | request.call(self, settings.opt(opt), data,
240 | function(err, info) {
241 | if(info && info.statusCode !== settings.success)
242 | err = new Error(info.statusCode + " " + info.status + "\n"
243 | + info.data);
244 | if(err)
245 | return callback.call(self, err);
246 | callback.call(self, null, info.data);
247 | });
248 | };
249 | }
250 |
251 | function request(options, data, callback) {
252 | // console.log(options);
253 | // console.log(data);
254 | var os = options["outputStream"];
255 | delete options["outputStream"];
256 |
257 | var self = this, req = https.request(options);
258 |
259 | req.on("response", function(response) {
260 | var info = "";
261 |
262 | response.on("end", function() {
263 | var res = this, ret = {};
264 | ret.statusCode = res.statusCode;
265 | ret.status = http.STATUS_CODES[res.statusCode];
266 | ret.data = info;
267 | callback.call(self, null, ret);
268 | });
269 | response.on("close", function() {
270 | response.emit("end");
271 | });
272 |
273 | if(os)
274 | return response.pipe(os);
275 |
276 | response.setEncoding("utf8");
277 |
278 | response.on("data", function(data) {
279 | info += data;
280 | });
281 | });
282 |
283 | req.on("error", function(err) {
284 | callback.call(self, err);
285 | });
286 |
287 | req.end(data);
288 | };
289 |
290 |
--------------------------------------------------------------------------------
/lib/spreadsheets/fork.js:
--------------------------------------------------------------------------------
1 | /***/
2 | var events = require("events");
3 |
4 | module.exports = function() {
5 | var self = this;
6 | var i, len, actors = Array.prototype.slice.call(arguments);
7 | var _ret = [], _cb = cb = actors.pop();
8 | var evt = new events.EventEmitter();
9 |
10 | evt.on("end", function(info, index) {
11 | _ret[index] = info;
12 | _cb = _cb.call(self, null, _ret);
13 | });
14 | evt.on("error", function(err) {
15 | cb.call(self, err);
16 | });
17 |
18 | try {
19 | for (i = 0, len = actors.length; i < len; i++) {
20 | _cb = wrap(_cb);
21 | process.nextTick(act(actors[i], i));
22 | }
23 | _cb = _cb.call(self, null, null);
24 | } catch (err) {
25 | _finally.call(self, err);
26 | }
27 |
28 | function act(actor, index) {
29 | return function() {
30 | try {
31 | var length = actor.length, args = [];
32 | if(length === 0)
33 | return evt.emit("end", actor.apply(self, args), index);
34 | args[length - 1] = function(err, info) {
35 | if(err)
36 | return evt.emit("error", err);
37 | evt.emit("end", info, index);
38 | };
39 | actor.apply(self, args);
40 | } catch (err) {
41 | evt.emit("error", err);
42 | }
43 | };
44 | }
45 |
46 | function wrap(fnc) {
47 | return function() {
48 | return fnc;
49 | };
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spreadsheets",
3 | "description": "A node.js client for Google Spreadsheets API",
4 | "version": "0.1.0",
5 | "author": "EastCloud ",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://EastCloud@github.com/EastCloud/node-spreadsheets.git"
9 | },
10 | "main": "./lib/spreadsheets/Spreadsheets",
11 | "engines": {
12 | "node": ">=0.4.0"
13 | },
14 | "licenses": [{
15 | "type": "Apache License, Version 2.0",
16 | "url": "http://www.apache.org/licenses/LICENSE-2.0"
17 | }]
18 | }
--------------------------------------------------------------------------------
/xml/batchcellentry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/xml/batchfeed.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/xml/cellentry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/xml/docentry.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/xml/worksheetentry.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------