You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

598 lines
16 KiB

  1. // Generated by CoffeeScript 1.6.2
  2. var Backbone, FileModel, Iconv, Model, TaskGroup, balUtil, jschardet, mime, pathUtil, safefs, typeChecker, _ref, _ref1,
  3. __hasProp = {}.hasOwnProperty,
  4. __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  5. __slice = [].slice;
  6. pathUtil = require('path');
  7. balUtil = require('bal-util');
  8. typeChecker = require('typechecker');
  9. TaskGroup = require('taskgroup').TaskGroup;
  10. safefs = require('safefs');
  11. mime = require('mime');
  12. jschardet = null;
  13. Iconv = null;
  14. _ref = require(__dirname + '/../base'), Backbone = _ref.Backbone, Model = _ref.Model;
  15. FileModel = (function(_super) {
  16. __extends(FileModel, _super);
  17. function FileModel() {
  18. _ref1 = FileModel.__super__.constructor.apply(this, arguments);
  19. return _ref1;
  20. }
  21. FileModel.prototype.outDirPath = null;
  22. FileModel.prototype.type = 'file';
  23. FileModel.prototype.stat = null;
  24. FileModel.prototype.data = null;
  25. FileModel.prototype.buffer = null;
  26. FileModel.prototype.meta = null;
  27. FileModel.prototype.defaults = {
  28. id: null,
  29. basename: null,
  30. extension: null,
  31. outExtension: null,
  32. extensions: null,
  33. filename: null,
  34. path: null,
  35. outPath: null,
  36. dirPath: null,
  37. outDirPath: null,
  38. outFilename: null,
  39. relativePath: null,
  40. relativeOutPath: null,
  41. relativeDirPath: null,
  42. relativeOutDirPath: null,
  43. relativeBase: null,
  44. contentType: null,
  45. outContentType: null,
  46. ctime: null,
  47. mtime: null,
  48. exists: null,
  49. encoding: null,
  50. source: null,
  51. content: null,
  52. title: null,
  53. name: null,
  54. date: null,
  55. slug: null,
  56. url: null,
  57. urls: null,
  58. ignored: false,
  59. standalone: false
  60. };
  61. FileModel.prototype.setData = function(data) {
  62. this.data = data;
  63. return this;
  64. };
  65. FileModel.prototype.getData = function() {
  66. return this.data;
  67. };
  68. FileModel.prototype.setBuffer = function(buffer) {
  69. this.buffer = buffer;
  70. return this;
  71. };
  72. FileModel.prototype.getBuffer = function() {
  73. return this.buffer;
  74. };
  75. FileModel.prototype.setStat = function(stat) {
  76. this.stat = stat;
  77. this.set({
  78. ctime: new Date(stat.ctime),
  79. mtime: new Date(stat.mtime)
  80. });
  81. return this;
  82. };
  83. FileModel.prototype.getStat = function() {
  84. return this.stat;
  85. };
  86. FileModel.prototype.getAttributes = function() {
  87. return this.toJSON();
  88. };
  89. FileModel.prototype.getMeta = function() {
  90. var args, _ref2;
  91. args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  92. if (this.meta === null) {
  93. this.meta = new Model();
  94. }
  95. if (args.length) {
  96. return (_ref2 = this.meta).get.apply(_ref2, args);
  97. } else {
  98. return this.meta;
  99. }
  100. };
  101. FileModel.prototype.setMeta = function(attrs) {
  102. this.getMeta().set(attrs);
  103. this.set(attrs);
  104. return this;
  105. };
  106. FileModel.prototype.setMetaDefaults = function(defaults) {
  107. this.getMeta().setDefaults(defaults);
  108. this.setDefaults(defaults);
  109. return this;
  110. };
  111. FileModel.prototype.getContent = function() {
  112. return this.get('content') || this.getBuffer();
  113. };
  114. FileModel.prototype.getOutContent = function() {
  115. return this.getContent();
  116. };
  117. FileModel.prototype.isText = function() {
  118. return this.get('encoding') !== 'binary';
  119. };
  120. FileModel.prototype.isBinary = function() {
  121. return this.get('encoding') === 'binary';
  122. };
  123. FileModel.prototype.setUrl = function(url) {
  124. this.addUrl(url);
  125. this.set({
  126. url: url
  127. });
  128. return this;
  129. };
  130. FileModel.prototype.addUrl = function(url) {
  131. var existingUrl, found, newUrl, urls, _i, _j, _len, _len1;
  132. if (url instanceof Array) {
  133. for (_i = 0, _len = url.length; _i < _len; _i++) {
  134. newUrl = url[_i];
  135. this.addUrl(newUrl);
  136. }
  137. } else if (url) {
  138. found = false;
  139. urls = this.get('urls');
  140. for (_j = 0, _len1 = urls.length; _j < _len1; _j++) {
  141. existingUrl = urls[_j];
  142. if (existingUrl === url) {
  143. found = true;
  144. break;
  145. }
  146. }
  147. if (!found) {
  148. urls.push(url);
  149. }
  150. this.trigger('change:urls', this, urls, {});
  151. this.trigger('change', this, {});
  152. }
  153. return this;
  154. };
  155. FileModel.prototype.removeUrl = function(userUrl) {
  156. var index, url, urls, _i, _len;
  157. urls = this.get('urls');
  158. for (index = _i = 0, _len = urls.length; _i < _len; index = ++_i) {
  159. url = urls[index];
  160. if (url === userUrl) {
  161. urls.splice(index, 1);
  162. break;
  163. }
  164. }
  165. return this;
  166. };
  167. FileModel.prototype.getPath = function(relativePath, parentPath) {
  168. var path, relativeDirPath;
  169. if (/^\./.test(relativePath)) {
  170. relativeDirPath = this.get('relativeDirPath');
  171. path = pathUtil.join(relativeDirPath, relativePath);
  172. } else {
  173. if (parentPath) {
  174. path = pathUtil.join(parentPath, relativePath);
  175. } else {
  176. path = relativePath;
  177. }
  178. }
  179. return path;
  180. };
  181. FileModel.prototype.initialize = function(attrs, opts) {
  182. var data, defaults, detectEncoding, meta, outDirPath, stat;
  183. detectEncoding = opts.detectEncoding, outDirPath = opts.outDirPath, stat = opts.stat, data = opts.data, meta = opts.meta;
  184. if (detectEncoding != null) {
  185. this.detectEncoding = detectEncoding;
  186. }
  187. if (outDirPath) {
  188. this.outDirPath = outDirPath;
  189. }
  190. defaults = {
  191. extensions: [],
  192. urls: [],
  193. id: this.cid
  194. };
  195. if (stat) {
  196. this.setStat(stat);
  197. } else {
  198. defaults.ctime = new Date();
  199. defaults.mtime = new Date();
  200. }
  201. this.set(defaults);
  202. if (attrs.data != null) {
  203. data = attrs.data;
  204. delete attrs.data;
  205. delete this.attributes.data;
  206. }
  207. if (data != null) {
  208. this.setData(data);
  209. }
  210. if (attrs.meta != null) {
  211. this.setMeta(attrs.meta);
  212. delete attrs.meta;
  213. }
  214. if (meta) {
  215. this.setMeta(meta);
  216. }
  217. return FileModel.__super__.initialize.apply(this, arguments);
  218. };
  219. FileModel.prototype.getActionArgs = function(opts, next) {
  220. if (typeChecker.isFunction(opts) && (next != null) === false) {
  221. next = opts;
  222. opts = {};
  223. } else {
  224. opts || (opts = {});
  225. }
  226. next || (next = opts.next || null);
  227. return {
  228. next: next,
  229. opts: opts
  230. };
  231. };
  232. FileModel.prototype.load = function(opts, next) {
  233. var buffer, data, exists, file, filePath, fullPath, tasks, _ref2, _ref3,
  234. _this = this;
  235. if (opts == null) {
  236. opts = {};
  237. }
  238. _ref2 = this.getActionArgs(opts, next), opts = _ref2.opts, next = _ref2.next;
  239. file = this;
  240. exists = (_ref3 = opts.exists) != null ? _ref3 : false;
  241. fullPath = this.get('fullPath');
  242. if (!fullPath) {
  243. filePath = this.get('relativePath') || this.get('fullPath') || this.get('filename');
  244. fullPath = this.get('fullPath') || filePath || null;
  245. file.set({
  246. fullPath: fullPath
  247. });
  248. }
  249. file.log('debug', "Loading the file: " + fullPath);
  250. tasks = new TaskGroup().setConfig({
  251. concurrency: 0
  252. }).once('complete', function(err) {
  253. if (err) {
  254. return next(err);
  255. }
  256. file.log('debug', "Loaded the file: " + fullPath);
  257. return file.parse(function(err) {
  258. if (err) {
  259. return next(err);
  260. }
  261. return file.normalize(function(err) {
  262. if (err) {
  263. return next(err);
  264. }
  265. return next(null, file.buffer);
  266. });
  267. });
  268. });
  269. data = file.getData();
  270. if (data != null) {
  271. buffer = new Buffer(data);
  272. file.setBuffer(buffer);
  273. }
  274. if (opts.stat) {
  275. file.setStat(opts.stat);
  276. }
  277. if (opts.buffer) {
  278. file.setBuffer(opts.buffer);
  279. }
  280. tasks.addTask(function(complete) {
  281. if (fullPath && exists && (opts.stat != null) === false) {
  282. return safefs.stat(fullPath, function(err, fileStat) {
  283. if (err) {
  284. return complete(err);
  285. }
  286. file.setStat(fileStat);
  287. return complete();
  288. });
  289. } else {
  290. return complete();
  291. }
  292. });
  293. tasks.addTask(function(complete) {
  294. if (fullPath && exists && (opts.buffer != null) === false) {
  295. return safefs.readFile(fullPath, function(err, buffer) {
  296. if (err) {
  297. return complete(err);
  298. }
  299. file.setBuffer(buffer);
  300. return complete();
  301. });
  302. } else {
  303. return complete();
  304. }
  305. });
  306. if (fullPath) {
  307. safefs.exists(fullPath, function(_exists) {
  308. exists = _exists;
  309. file.set({
  310. exists: exists
  311. });
  312. return tasks.run();
  313. });
  314. } else {
  315. tasks.run();
  316. }
  317. return this;
  318. };
  319. FileModel.prototype.parse = function(opts, next) {
  320. var buffer, changes, content, encoding, err, fullPath, isText, source, _ref2, _ref3, _ref4;
  321. if (opts == null) {
  322. opts = {};
  323. }
  324. _ref2 = this.getActionArgs(opts, next), opts = _ref2.opts, next = _ref2.next;
  325. buffer = this.getBuffer();
  326. fullPath = this.get('fullPath');
  327. encoding = this.get('encoding') || null;
  328. changes = {};
  329. if ((encoding != null) === false || opts.reencode === true) {
  330. isText = balUtil.isTextSync(fullPath, buffer);
  331. if (isText === true) {
  332. if (this.detectEncoding) {
  333. if (jschardet == null) {
  334. jschardet = require('jschardet');
  335. }
  336. try {
  337. if (Iconv == null) {
  338. Iconv = require('iconv').Iconv;
  339. }
  340. } catch (_error) {
  341. err = _error;
  342. Iconv = null;
  343. }
  344. if (encoding == null) {
  345. encoding = ((_ref3 = jschardet.detect(buffer)) != null ? _ref3.encoding : void 0) || 'utf8';
  346. }
  347. } else {
  348. if (encoding == null) {
  349. encoding = 'utf8';
  350. }
  351. }
  352. if ((_ref4 = encoding.toLowerCase()) !== 'ascii' && _ref4 !== 'utf8' && _ref4 !== 'utf-8') {
  353. if (Iconv != null) {
  354. this.log('info', "Converting encoding " + encoding + " to UTF-8 on " + fullPath);
  355. try {
  356. buffer = new Iconv(encoding, 'utf8').convert(buffer);
  357. } catch (_error) {
  358. err = _error;
  359. this.log('warn', "Encoding conversion failed, therefore we cannot convert the encoding " + encoding + " to UTF-8 on " + fullPath);
  360. }
  361. } else {
  362. this.log('warn', "Iconv did not load, therefore we cannot convert the encoding " + encoding + " to UTF-8 on " + fullPath);
  363. }
  364. }
  365. changes.encoding = encoding;
  366. } else {
  367. encoding = 'binary';
  368. changes.encoding = encoding;
  369. }
  370. }
  371. if (encoding === 'binary') {
  372. content = source = '';
  373. changes.content = content;
  374. changes.source = source;
  375. } else {
  376. source = buffer.toString('utf8');
  377. content = source;
  378. changes.content = content;
  379. changes.source = source;
  380. }
  381. this.set(changes);
  382. next();
  383. return this;
  384. };
  385. FileModel.prototype.normalize = function(opts, next) {
  386. var basename, changes, contentType, date, extension, extensions, filename, fullDirPath, fullPath, mtime, relativeBase, relativeDirPath, relativePath, _ref2;
  387. if (opts == null) {
  388. opts = {};
  389. }
  390. _ref2 = this.getActionArgs(opts, next), opts = _ref2.opts, next = _ref2.next;
  391. changes = {};
  392. basename = this.get('basename');
  393. filename = this.get('filename');
  394. fullPath = this.get('fullPath');
  395. extensions = this.get('extensions');
  396. relativePath = this.get('relativePath');
  397. mtime = this.get('mtime');
  398. date = this.getMeta('date') || null;
  399. if (fullPath) {
  400. changes.filename = filename = pathUtil.basename(fullPath);
  401. changes.outFilename = filename;
  402. }
  403. if (filename) {
  404. if (filename[0] === '.') {
  405. basename = filename.replace(/^(\.[^\.]+)\..*$/, '$1');
  406. } else {
  407. basename = filename.replace(/\..*$/, '');
  408. }
  409. changes.basename = basename;
  410. if ((extensions != null) === false || extensions.length === 0) {
  411. extensions = filename.split(/\./g);
  412. extensions.shift();
  413. }
  414. changes.extensions = extensions;
  415. if (extensions.length) {
  416. extension = extensions[extensions.length - 1];
  417. } else {
  418. extension = null;
  419. }
  420. changes.extension = extension;
  421. changes.outExtension = extension;
  422. }
  423. if (fullPath) {
  424. changes.fullDirPath = fullDirPath = pathUtil.dirname(fullPath) || '';
  425. changes.contentType = contentType = mime.lookup(fullPath);
  426. changes.outContentType = contentType;
  427. }
  428. if (relativePath) {
  429. changes.relativeDirPath = relativeDirPath = pathUtil.dirname(relativePath).replace(/^\.$/, '') || '';
  430. changes.relativeBase = relativeBase = relativeDirPath ? pathUtil.join(relativeDirPath, basename) : basename;
  431. }
  432. if (!date && mtime) {
  433. changes.date = date = mtime;
  434. }
  435. this.set(changes);
  436. next();
  437. return this;
  438. };
  439. FileModel.prototype.contextualize = function(opts, next) {
  440. var changes, filename, meta, name, outDirPath, outPath, relativeBase, relativeDirPath, relativeOutDirPath, relativeOutPath, relativePath, slug, url, _ref2;
  441. if (opts == null) {
  442. opts = {};
  443. }
  444. _ref2 = this.getActionArgs(opts, next), opts = _ref2.opts, next = _ref2.next;
  445. changes = {};
  446. meta = this.getMeta();
  447. relativePath = this.get('relativePath');
  448. relativeDirPath = this.get('relativeDirPath');
  449. relativeBase = this.get('relativeBase');
  450. filename = this.get('filename');
  451. outPath = this.get('outPath');
  452. outDirPath = this.get('outDirPath');
  453. name = meta.get('name') || null;
  454. slug = meta.get('slug') || null;
  455. url = meta.get('url') || null;
  456. if (!url && relativePath) {
  457. url = "/" + relativePath;
  458. this.setUrl(url);
  459. }
  460. if (!slug && relativeBase) {
  461. changes.slug = slug = balUtil.generateSlugSync(relativeBase);
  462. }
  463. if (!name && filename) {
  464. changes.name = name = filename;
  465. }
  466. if (this.outDirPath && relativePath) {
  467. if (relativeDirPath != null) {
  468. changes.relativeOutDirPath = relativeOutDirPath = relativeDirPath;
  469. }
  470. changes.relativeOutPath = relativeOutPath = relativePath;
  471. changes.outPath = outPath = pathUtil.join(this.outDirPath, relativePath);
  472. if (outPath) {
  473. changes.outDirPath = outDirPath = pathUtil.dirname(outPath);
  474. }
  475. }
  476. this.set(changes);
  477. next();
  478. return this;
  479. };
  480. FileModel.prototype.write = function(opts, next) {
  481. var err, file, _ref2, _ref3;
  482. _ref2 = this.getActionArgs(opts, next), opts = _ref2.opts, next = _ref2.next;
  483. file = this;
  484. opts.path || (opts.path = this.get('outPath'));
  485. opts.encoding || (opts.encoding = this.get('encoding'));
  486. opts.content || (opts.content = this.getContent());
  487. opts.type || (opts.type = 'file');
  488. if (!opts.path) {
  489. next();
  490. return this;
  491. }
  492. if ((_ref3 = opts.encoding.toLowerCase()) !== 'ascii' && _ref3 !== 'utf8' && _ref3 !== 'utf-8' && _ref3 !== 'binary') {
  493. if (Iconv != null) {
  494. this.log('info', "Converting encoding UTF-8 to " + opts.encoding + " on " + opts.path);
  495. try {
  496. opts.content = new Iconv('utf8', opts.encoding).convert(opts.content);
  497. } catch (_error) {
  498. err = _error;
  499. this.log('warn', "Encoding conversion failed, therefore we cannot convert the encoding UTF-8 to " + opts.encoding + " on " + opts.path);
  500. }
  501. } else {
  502. this.log('warn', "Iconv did not load, therefore we cannot convert the encoding UTF-8 to " + opts.encoding + " on " + opts.path);
  503. }
  504. }
  505. file.log('debug', "Writing the " + opts.type + ": " + opts.path + " " + opts.encoding);
  506. safefs.writeFile(opts.path, opts.content, function(err) {
  507. if (err) {
  508. return next(err);
  509. }
  510. file.log('debug', "Wrote the " + opts.type + ": " + opts.path + " " + opts.encoding);
  511. return next();
  512. });
  513. return this;
  514. };
  515. FileModel.prototype["delete"] = function(next) {
  516. var file, fileOutPath;
  517. file = this;
  518. fileOutPath = this.get('outPath');
  519. if (!fileOutPath) {
  520. next();
  521. return this;
  522. }
  523. file.log('debug', "Delete the file: " + fileOutPath);
  524. safefs.exists(fileOutPath, function(exists) {
  525. if (!exists) {
  526. return next();
  527. }
  528. return safefs.unlink(fileOutPath, function(err) {
  529. if (err) {
  530. return next(err);
  531. }
  532. file.log('debug', "Deleted the file: " + fileOutPath);
  533. return next();
  534. });
  535. });
  536. return this;
  537. };
  538. return FileModel;
  539. })(Model);
  540. module.exports = FileModel;