bone_tree 0.5.6 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +26 -3
  2. data/Gemfile +5 -12
  3. data/Rakefile +7 -9
  4. data/bone_tree.gemspec +30 -14
  5. data/config.rb +8 -0
  6. data/lib/assets/javascripts/bone_tree.js +277 -981
  7. data/lib/assets/stylesheets/bone_tree.css +108 -0
  8. data/lib/bone_tree/rails.rb +5 -0
  9. data/lib/bone_tree/sprockets.rb +3 -0
  10. data/lib/{version.rb → bone_tree/version.rb} +1 -1
  11. data/lib/bone_tree.rb +11 -6
  12. data/source/index.html.haml +10 -382
  13. data/source/javascripts/{_backbone.js → backbone.js} +282 -142
  14. data/source/javascripts/bone_tree/models/directory.js.coffee +109 -0
  15. data/source/javascripts/bone_tree/models/file.js.coffee +12 -0
  16. data/source/javascripts/bone_tree/models/node.js.coffee +24 -0
  17. data/source/javascripts/bone_tree/models/settings.js.coffee +5 -0
  18. data/source/javascripts/bone_tree/{_namespace.js.coffee → namespace.js.coffee} +1 -1
  19. data/source/javascripts/bone_tree/views/directory.js.coffee +56 -0
  20. data/source/javascripts/bone_tree/views/file.js.coffee +25 -0
  21. data/source/javascripts/bone_tree/views/tree.js.coffee +87 -0
  22. data/source/javascripts/bone_tree.js.coffee +2 -1
  23. data/source/stylesheets/bone_tree.css.sass +0 -38
  24. data/spec/javascripts/directory_spec.coffee +26 -0
  25. data/spec/javascripts/helpers/spec_helper.coffee +3 -4
  26. data/spec/javascripts/sorting_spec.coffee +12 -0
  27. data/spec/javascripts/support/jasmine.yml +6 -4
  28. metadata +143 -36
  29. data/Gemfile.lock +0 -190
  30. data/docs/index.html +0 -222
  31. data/docs/resources/base.css +0 -70
  32. data/docs/resources/index.css +0 -20
  33. data/docs/resources/module.css +0 -24
  34. data/docs/source/javascripts/bone_tree/_namespace.js.html +0 -45
  35. data/docs/source/javascripts/bone_tree/models/_directory.js.html +0 -126
  36. data/docs/source/javascripts/bone_tree/models/_file.js.html +0 -112
  37. data/docs/source/javascripts/bone_tree/models/_nodes.js.html +0 -174
  38. data/docs/source/javascripts/bone_tree/models/_settings.js.html +0 -75
  39. data/docs/source/javascripts/bone_tree/views/_directory.js.html +0 -94
  40. data/docs/source/javascripts/bone_tree/views/_file.js.html +0 -82
  41. data/docs/source/javascripts/bone_tree/views/_menu.js.html +0 -110
  42. data/docs/source/javascripts/bone_tree/views/_tree.js.html +0 -432
  43. data/source/javascripts/_jquery.min.js +0 -5
  44. data/source/javascripts/_underscore.js +0 -999
  45. data/source/javascripts/bone_tree/models/_directory.js.coffee +0 -63
  46. data/source/javascripts/bone_tree/models/_file.js.coffee +0 -55
  47. data/source/javascripts/bone_tree/models/_nodes.js.coffee +0 -117
  48. data/source/javascripts/bone_tree/models/_settings.js.coffee +0 -25
  49. data/source/javascripts/bone_tree/views/_directory.js.coffee +0 -73
  50. data/source/javascripts/bone_tree/views/_file.js.coffee +0 -51
  51. data/source/javascripts/bone_tree/views/_menu.js.coffee +0 -97
  52. data/source/javascripts/bone_tree/views/_tree.js.coffee +0 -498
  53. data/spec/javascripts/directory_view_spec.coffee +0 -91
  54. data/spec/javascripts/file_view_spec.coffee +0 -70
  55. data/spec/javascripts/menu_view_spec.coffee +0 -42
  56. data/spec/javascripts/nodes_spec.coffee +0 -37
  57. data/spec/javascripts/tree_view_spec.coffee +0 -39
@@ -1,7 +1,7 @@
1
1
  (function() {
2
2
  var __slice = [].slice;
3
3
 
4
- window.BoneTree = {};
4
+ window.BoneTree || (window.BoneTree = {});
5
5
 
6
6
  BoneTree.namespace = function(target, name, block) {
7
7
  var item, top, _i, _len, _ref, _ref1;
@@ -21,99 +21,36 @@
21
21
  (function() {
22
22
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
23
23
  __hasProp = {}.hasOwnProperty,
24
- __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; };
24
+ __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; };
25
25
 
26
26
  BoneTree.namespace("BoneTree.Models", function(Models) {
27
27
  Models.Node = (function(_super) {
28
28
 
29
29
  __extends(Node, _super);
30
30
 
31
- Node.name = 'Node';
32
-
33
31
  function Node() {
34
- this.nameWithExtension = __bind(this.nameWithExtension, this);
32
+ this.name = __bind(this.name, this);
33
+
34
+ this.isFile = __bind(this.isFile, this);
35
35
 
36
- this.constantize = __bind(this.constantize, this);
36
+ this.isDirectory = __bind(this.isDirectory, this);
37
37
  return Node.__super__.constructor.apply(this, arguments);
38
38
  }
39
39
 
40
- /*
41
- Internal: An abstract super class for File and Directory objects to inherit from.
42
- */
43
-
44
-
45
40
  Node.prototype.initialize = function() {
46
- /*
47
- Internal: Initialize a new Node object. Set it up to contain a collection of
48
- children nodes.
49
- */
50
41
  return this.collection = new Models.Nodes;
51
42
  };
52
43
 
53
- Node.prototype.constantize = function() {
54
- /*
55
- Public: Returns a String with the nodeType capitalized so that it may be used
56
- to instatiate the appropriate view type
57
-
58
- Examples
59
-
60
- file = new BoneTree.Models.File
61
- directory = new BoneTree.Models.Directory
62
-
63
- file.constantize()
64
- # => 'File'
65
-
66
- directory.constantize()
67
- # => 'Directory'
68
-
69
- # use it to create a new view of the appropriate type
70
- view = new BoneTree.Views[file.constantize()]
71
-
72
- Returns a String of the nodeType with the first letter capitalized.
73
- */
74
-
75
- var nodeType;
76
- nodeType = this.get('nodeType');
77
- return nodeType[0].toUpperCase() + nodeType.substring(1);
44
+ Node.prototype.isDirectory = function() {
45
+ return this instanceof Models.Directory;
78
46
  };
79
47
 
80
- Node.prototype.nameWithExtension = function() {
81
- /*
82
- Public: Returns the node name with the extension if it has
83
- one and just the node name if there is no extension.
84
-
85
- Examples
86
-
87
- file = new BoneTree.Models.File
88
- name: "file"
89
- extension: "coffee"
90
-
91
- noExt = new BoneTree.Models.File
92
- name: "file2"
93
-
94
- directory = new BoneTree.Model.Directory
95
- name: "source"
96
-
97
- file.nameWithExtension()
98
- # => "file.coffee"
99
-
100
- noExt.nameWithExtension()
101
- # => "file2"
102
-
103
- directory.nameWithExtension()
104
- # => "source"
105
-
106
- Returns a String. If the extension exists then the node name plus the extension
107
- are returned. If there is no extension, then just the node name is returned.
108
- */
109
-
110
- var extension, name, _ref;
111
- _ref = this.attributes, extension = _ref.extension, name = _ref.name;
112
- extension || (extension = "");
113
- if (extension !== "") {
114
- extension = "." + extension;
115
- }
116
- return name + extension;
48
+ Node.prototype.isFile = function() {
49
+ return this instanceof Models.File;
50
+ };
51
+
52
+ Node.prototype.name = function() {
53
+ return this.get('path').split('/').last();
117
54
  };
118
55
 
119
56
  return Node;
@@ -123,47 +60,18 @@
123
60
 
124
61
  __extends(Nodes, _super);
125
62
 
126
- Nodes.name = 'Nodes';
127
-
128
63
  function Nodes() {
129
64
  return Nodes.__super__.constructor.apply(this, arguments);
130
65
  }
131
66
 
132
- /*
133
- Internal: A collection of node models. Since Node is an abstract super
134
- class, in practice this collection will hold File objects
135
- and Directory objects.
136
- */
137
-
138
-
139
67
  Nodes.prototype.comparator = function(node) {
140
- /*
141
- Internal: Function that determines how the file tree is sorted. Directories
142
- are sorted before files. After node type sort
143
- priority, nodes are sorted by name.
144
-
145
- Examples
146
-
147
- tree.addFile('/source/file1.coffee')
148
- tree.addFile('/source/file2.coffee')
149
- tree.addFile('main.coffee')
150
-
151
- tree.render()
152
-
153
- # even though 'main' comes before 'source' alphabetically it is placed
154
- # after the 'source' directory due to the sortPriority of the Directory object.
155
- tree.toAscii()
156
- # => "
157
- -source
158
- -file1.coffee
159
- -file2.coffee
160
- -main.coffee
161
- "
162
- */
163
-
164
- var name, sortPriority, _ref;
165
- _ref = node.attributes, name = _ref.name, sortPriority = _ref.sortPriority;
166
- return sortPriority + name;
68
+ var sortPriority;
69
+ if (node.isDirectory()) {
70
+ sortPriority = 0;
71
+ } else {
72
+ sortPriority = 1;
73
+ }
74
+ return sortPriority + node.name();
167
75
  };
168
76
 
169
77
  Nodes.prototype.model = Models.Node;
@@ -177,206 +85,225 @@
177
85
  (function() {
178
86
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
179
87
  __hasProp = {}.hasOwnProperty,
180
- __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; };
88
+ __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; },
89
+ __slice = [].slice;
181
90
 
182
91
  BoneTree.namespace("BoneTree.Models", function(Models) {
183
- Models.Directory = (function(_super) {
92
+ return Models.Directory = (function(_super) {
184
93
 
185
94
  __extends(Directory, _super);
186
95
 
187
- Directory.name = 'Directory';
188
-
189
96
  function Directory() {
190
- this.toggleOpen = __bind(this.toggleOpen, this);
97
+ this.updateChildren = __bind(this.updateChildren, this);
98
+
99
+ this.toAscii = __bind(this.toAscii, this);
100
+
101
+ this.toArray = __bind(this.toArray, this);
102
+
103
+ this.getFile = __bind(this.getFile, this);
104
+
105
+ this.files = __bind(this.files, this);
106
+
107
+ this.getDirectory = __bind(this.getDirectory, this);
108
+
109
+ this.directories = __bind(this.directories, this);
110
+
111
+ this.remove = __bind(this.remove, this);
112
+
113
+ this.add = __bind(this.add, this);
191
114
  return Directory.__super__.constructor.apply(this, arguments);
192
115
  }
193
116
 
194
- /*
195
- Public: Object representing a directory.
196
-
197
- * defaults
198
- * name - A String naming the directory (default: "New Directory").
199
- * sortPriority - A String representing the priority with which the
200
- node is sorted. Directories have sortPriority "0"
201
- allowing them to be sorted before Files which have
202
- sortPriority "1".
203
- * nodeType - A String denoting what type of node this object is.
204
- The two types are "file" and "directory".
205
- * open - The state of the directory. Controls whether or not
206
- to display files and directories contained within this
207
- Directory (default: false).
208
- */
209
-
210
-
211
- Directory.prototype.defaults = {
212
- name: "New Directory",
213
- open: false,
214
- sortPriority: "0",
215
- nodeType: "directory"
117
+ Directory.prototype.add = function(filePath, fileData) {
118
+ var Directory, File, currentDirectory, dirs, file, fileName, _i, _ref;
119
+ if (fileData == null) {
120
+ fileData = {};
121
+ }
122
+ Directory = Models.Directory, File = Models.File;
123
+ _ref = filePath.split('/'), dirs = 2 <= _ref.length ? __slice.call(_ref, 0, _i = _ref.length - 1) : (_i = 0, []), fileName = _ref[_i++];
124
+ currentDirectory = this;
125
+ dirs.each(function(directoryName) {
126
+ var directory;
127
+ if (directoryName === '') {
128
+ return;
129
+ }
130
+ directory = currentDirectory.directories().select(function(dir) {
131
+ return dir.name() === directoryName;
132
+ }).first();
133
+ if (!directory) {
134
+ directory = new Directory({
135
+ path: filePath.substring(0, filePath.indexOf(directoryName) + directoryName.length)
136
+ });
137
+ currentDirectory.collection.add(directory);
138
+ }
139
+ return currentDirectory = directory;
140
+ });
141
+ if (file = currentDirectory.getFile(fileName)) {
142
+ return file.set(fileData);
143
+ } else {
144
+ file = new File(_.extend(fileData, {
145
+ path: filePath
146
+ }));
147
+ currentDirectory.collection.add(file);
148
+ return file;
149
+ }
150
+ };
151
+
152
+ Directory.prototype.remove = function(path) {
153
+ var directory, file;
154
+ if (file = this.getFile(path)) {
155
+ return file.destroy();
156
+ } else if (directory = this.getDirectory(path)) {
157
+ return directory.destroy();
158
+ }
216
159
  };
217
160
 
218
- Directory.prototype.toggleOpen = function() {
219
- /*
220
- Public: Toggle the open state of this Directory.
221
-
222
- Examples
223
-
224
- dir = new BoneTree.Models.Directory
225
-
226
- dir.get('open')
227
- # => false
228
-
229
- dir.toggleOpen()
230
- dir.get('open')
231
- # => true
232
-
233
- Returns this Directory.
234
- */
235
-
236
- var currentState;
237
- currentState = this.get('open');
238
- return this.set({
239
- open: !currentState
161
+ Directory.prototype.directories = function() {
162
+ return this.collection.filter(function(node) {
163
+ return node.isDirectory();
164
+ });
165
+ };
166
+
167
+ Directory.prototype.getDirectory = function(directoryPath) {
168
+ var first, match, rest, _ref;
169
+ _ref = directoryPath.split('/'), first = _ref[0], rest = 2 <= _ref.length ? __slice.call(_ref, 1) : [];
170
+ match = this.collection.find(function(node) {
171
+ return node.name() === first;
172
+ });
173
+ if (match != null ? match.isDirectory() : void 0) {
174
+ if (rest.length) {
175
+ return match.getDirectory(rest.join('/'));
176
+ } else {
177
+ return match;
178
+ }
179
+ }
180
+ return void 0;
181
+ };
182
+
183
+ Directory.prototype.files = function() {
184
+ return this.collection.filter(function(node) {
185
+ return node.isFile();
186
+ });
187
+ };
188
+
189
+ Directory.prototype.getFile = function(filePath) {
190
+ var first, match, rest, _ref;
191
+ _ref = filePath.split('/'), first = _ref[0], rest = 2 <= _ref.length ? __slice.call(_ref, 1) : [];
192
+ match = this.collection.find(function(node) {
193
+ return node.name() === first;
194
+ });
195
+ if (match != null ? match.isDirectory() : void 0) {
196
+ if (rest.length) {
197
+ return match.getFile(rest.join('/'));
198
+ } else {
199
+ return void 0;
200
+ }
201
+ } else {
202
+ return match;
203
+ }
204
+ };
205
+
206
+ Directory.prototype.toArray = function() {
207
+ var results,
208
+ _this = this;
209
+ results = [this];
210
+ this.collection.each(function(node) {
211
+ return results = results.concat(node.toArray());
212
+ });
213
+ return results;
214
+ };
215
+
216
+ Directory.prototype.toAscii = function(indentation) {
217
+ var nodeAscii;
218
+ if (indentation == null) {
219
+ indentation = '';
220
+ }
221
+ nodeAscii = ["" + indentation + "+" + (this.name())];
222
+ this.collection.each(function(node) {
223
+ return nodeAscii.push(node.toAscii(indentation + ' '));
224
+ });
225
+ return nodeAscii.join('\n');
226
+ };
227
+
228
+ Directory.prototype.updateChildren = function(previousPath, newPath) {
229
+ var directoryPath,
230
+ _this = this;
231
+ this.directories().each(function(directory) {
232
+ return directory.updateChildren(previousPath, newPath);
233
+ });
234
+ directoryPath = this.get('path');
235
+ if (directoryPath.indexOf(newPath) === -1) {
236
+ this.set({
237
+ path: this.get('path').replace(previousPath, newPath)
238
+ });
239
+ }
240
+ return this.files().each(function(file) {
241
+ var filePath;
242
+ filePath = file.get('path');
243
+ if (filePath.indexOf(newPath) === -1) {
244
+ return file.set({
245
+ path: file.get('path').replace(previousPath, newPath)
246
+ });
247
+ }
240
248
  });
241
249
  };
242
250
 
243
251
  return Directory;
244
252
 
245
253
  })(Models.Node);
246
- return Models.Directory.find = function(currentDirectory, name) {
247
- /*
248
- Internal: Check to see if there is a directory with the matching name
249
- contained within currentDirectory.
250
-
251
- * currentDirectory - A Directory object to search for the matching directory name.
252
-
253
- * name - A String name used to check for matching directory
254
- names in currentDirectory.
255
-
256
- Returns The Directory object with the matching name if it exists and undefined otherwise.
257
- */
258
- return currentDirectory.collection.find(function(dir) {
259
- return dir.get('name') === name;
260
- });
261
- };
262
254
  });
263
255
 
264
256
  }).call(this);
265
257
  (function() {
266
- var __hasProp = {}.hasOwnProperty,
267
- __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; },
268
- __slice = [].slice;
258
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
259
+ __hasProp = {}.hasOwnProperty,
260
+ __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; };
269
261
 
270
262
  BoneTree.namespace("BoneTree.Models", function(Models) {
271
- Models.File = (function(_super) {
263
+ return Models.File = (function(_super) {
272
264
 
273
265
  __extends(File, _super);
274
266
 
275
- File.name = 'File';
276
-
277
267
  function File() {
268
+ this.toAscii = __bind(this.toAscii, this);
269
+
270
+ this.toArray = __bind(this.toArray, this);
278
271
  return File.__super__.constructor.apply(this, arguments);
279
272
  }
280
273
 
281
- /*
282
- Public: Object representing file data in the tree.
283
-
284
- * defaults
285
- * name - A String naming the file (default: "New File").
286
- * sortPriority - A String representing the priority with which the
287
- node is sorted. Directories have sortPriority "0"
288
- allowing them to be sorted before Files which have
289
- sortPriority "1".
290
- * nodeType - A String denoting what type of node this object is.
291
- The two types are "file" and "directory".
292
- */
293
-
294
-
295
- File.prototype.defaults = {
296
- name: "New File",
297
- sortPriority: "1",
298
- nodeType: "file"
274
+ File.prototype.toArray = function() {
275
+ return [this];
276
+ };
277
+
278
+ File.prototype.toAscii = function(indentation) {
279
+ if (indentation == null) {
280
+ indentation = '';
281
+ }
282
+ return "" + indentation + "-" + (this.name());
299
283
  };
300
284
 
301
285
  return File;
302
286
 
303
287
  })(Models.Node);
304
- return Models.File.createFromFileName = function(fileName, fileData) {
305
- /*
306
- Public: Class method to create a new File object based on the fileName
307
- and fileData passed in.
308
-
309
- * fileName - A String representing the name of the file to be created.
310
- files with '.' in the name will be parsed out and only the
311
- string after the final '.' will be considered the extension.
312
-
313
- * fileData - An Object of attributes to associate with the file.
314
-
315
- Examples
316
-
317
- data = {
318
- contents: alert 'this is the code in the file'
319
- createdAt: 1330846900589
320
- language: 'CoffeeScript'
321
- }
322
-
323
- BoneTree.Models.File.createFromFileName('example.coffee', data)
324
- # => <File>
325
-
326
- Returns the File object just created.
327
- */
328
-
329
- var data, extension, name, names, _i, _ref;
330
- _ref = fileName.split("."), names = 2 <= _ref.length ? __slice.call(_ref, 0, _i = _ref.length - 1) : (_i = 0, []), extension = _ref[_i++];
331
- name = names.join('.');
332
- data = _.extend({}, fileData, {
333
- name: name,
334
- extension: extension
335
- });
336
- return new Models.File(data);
337
- };
338
288
  });
339
289
 
340
290
  }).call(this);
341
291
  (function() {
342
292
  var __hasProp = {}.hasOwnProperty,
343
- __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; };
293
+ __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; };
344
294
 
345
295
  BoneTree.namespace("BoneTree.Models", function(Models) {
346
296
  return Models.Settings = (function(_super) {
347
297
 
348
298
  __extends(Settings, _super);
349
299
 
350
- Settings.name = 'Settings';
351
-
352
300
  function Settings() {
353
301
  return Settings.__super__.constructor.apply(this, arguments);
354
302
  }
355
303
 
356
- /*
357
- Internal: A configuration object. Consumers of the API don't need to
358
- worry about this. It is used internally to manage the options
359
- passed into the file tree.
360
-
361
- * defaults
362
- * beforeAdd - A Function that is invoked before each file is added to the tree.
363
- It is passed the raw file attributes and should return true if
364
- that file should be added to the tree and false if not. The
365
- default implementation is to return true.
366
- * confirmDeletes - A Boolean. If true, the tree will prompt the user, making
367
- sure they want to delete the file (default: false).
368
- * showExtensions - A Boolean. If true, files display their extensions. Internally,
369
- extensions are always kept track of but by default they are
370
- hidden (default: false).
371
- * viewCache - An Object that stores views when they are created and is used
372
- to look them up to prevent extra views from being created.
373
- */
374
-
375
-
376
304
  Settings.prototype.defaults = {
377
305
  confirmDeletes: false,
378
- showExtensions: false,
379
- viewCache: {}
306
+ showExtensions: false
380
307
  };
381
308
 
382
309
  return Settings;
@@ -388,16 +315,16 @@
388
315
  (function() {
389
316
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
390
317
  __hasProp = {}.hasOwnProperty,
391
- __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; };
318
+ __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; };
392
319
 
393
320
  BoneTree.namespace("BoneTree.Views", function(Views) {
394
321
  return Views.Directory = (function(_super) {
395
322
 
396
323
  __extends(Directory, _super);
397
324
 
398
- Directory.name = 'Directory';
399
-
400
325
  function Directory() {
326
+ this.toggleOpen = __bind(this.toggleOpen, this);
327
+
401
328
  this.displayChildren = __bind(this.displayChildren, this);
402
329
 
403
330
  this.render = __bind(this.render, this);
@@ -406,80 +333,59 @@
406
333
  return Directory.__super__.constructor.apply(this, arguments);
407
334
  }
408
335
 
409
- /*
410
- Internal: View that renders a Directory node and controls its behavior (class: 'directory', tag: 'ul').
411
- */
412
-
413
-
414
336
  Directory.prototype.className = 'directory';
415
337
 
416
338
  Directory.prototype.tagName = 'ul';
417
339
 
418
340
  Directory.prototype.initialize = function(options) {
419
- /*
420
- Internal: Initialize a new directory node. Adds associated model cid to the
421
- view element. Binds change handler to the `change:open` event that
422
- toggles the display of directory contents. Binds change handler to
423
- the `change:name` event that re-renders a sorted file tree.
424
-
425
- * options - Passes in settings object, which is used for access to the
426
- tree view root in order to proxy events to it.
427
- */
428
-
429
341
  var _this = this;
430
- this.settings = options.settings;
342
+ this.tree = options.tree;
431
343
  this.$el.attr('data-cid', this.model.cid);
432
- this.model.bind('change:open', function(model, open) {
433
- return _this.displayChildren(open);
434
- });
435
- this.model.bind('change:name', function(model, name) {
436
- var treeView;
437
- treeView = _this.settings.get('treeView');
438
- return treeView.render().trigger('rename', model, name);
344
+ this.open = false;
345
+ this.model.bind('change:path', function(model) {
346
+ var newPath, path, previousPath;
347
+ _this.tree.render().trigger('rename', model, name);
348
+ previousPath = model.previous('path');
349
+ if ((path = model.get('path')).indexOf('/') === 0) {
350
+ newPath = path.replace('/', '');
351
+ } else {
352
+ newPath = path;
353
+ }
354
+ return model.updateChildren(previousPath, newPath);
439
355
  });
440
356
  this.model.collection.bind('add', this.render);
441
357
  this.model.collection.bind('remove', function(model, collection) {
442
- _this.settings.get('treeView').trigger('remove', model);
358
+ _this.tree.trigger('remove', model);
443
359
  return _this.render();
444
360
  });
445
- return this.displayChildren(this.model.get('open'));
361
+ return this.displayChildren(this.open);
446
362
  };
447
363
 
448
364
  Directory.prototype.appendView = function(node) {
449
- /*
450
- Internal: Appends a view based on the underlying node model to this view.
451
-
452
- node - A Node model. Either a File or a Directory. This is the model the
453
- created view will be associated with.
454
- */
455
-
456
365
  var view;
457
- view = this.settings.get('treeView').findOrCreateView(node);
366
+ view = this.tree.findOrCreateView(node);
458
367
  return this.$el.append(view.render().$el);
459
368
  };
460
369
 
461
370
  Directory.prototype.render = function() {
462
- /*
463
- Internal: Set the text of the view element based on the underlying model name.
464
-
465
- Returns `this` view.
466
- */
467
- this.$el.text(this.model.get('name'));
468
- this.model.collection.sort().each(this.appendView);
371
+ this.$el.text(this.model.name());
372
+ this.model.collection.each(this.appendView);
373
+ this.displayChildren(this.$el.hasClass('open'));
469
374
  return this;
470
375
  };
471
376
 
472
377
  Directory.prototype.displayChildren = function(open) {
473
- /*
474
- Internal: Toggles display of the children Files or Diretories of this view.
475
- */
476
-
477
378
  var fileDirChildren;
478
379
  fileDirChildren = this.$el.children('.directory, .file');
479
380
  this.$el.toggleClass('open', open);
480
381
  return fileDirChildren.toggle(open);
481
382
  };
482
383
 
384
+ Directory.prototype.toggleOpen = function() {
385
+ this.open = !this.open;
386
+ return this.displayChildren(this.open);
387
+ };
388
+
483
389
  return Directory;
484
390
 
485
391
  })(Backbone.View);
@@ -489,70 +395,39 @@
489
395
  (function() {
490
396
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
491
397
  __hasProp = {}.hasOwnProperty,
492
- __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; };
398
+ __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; };
493
399
 
494
400
  BoneTree.namespace("BoneTree.Views", function(Views) {
495
401
  return Views.File = (function(_super) {
496
402
 
497
403
  __extends(File, _super);
498
404
 
499
- File.name = 'File';
500
-
501
405
  function File() {
502
406
  this.render = __bind(this.render, this);
503
407
  return File.__super__.constructor.apply(this, arguments);
504
408
  }
505
409
 
506
- /*
507
- Internal: View that renders a File node and controls its behavior (class: 'file', tag: 'li').
508
- */
509
-
510
-
511
- File.prototype.className = 'file';
512
-
513
410
  File.prototype.tagName = 'li';
514
411
 
515
412
  File.prototype.initialize = function(options) {
516
- /*
517
- Internal: Initialize a new file node. Adds associated model cid to the
518
- view element. Binds change handlers to the `change:name` and
519
- `change:extension` events. These take care of resorting the file
520
- nodes.
521
-
522
- * options - Passes in settings object, which is used to control
523
- whether or not file extensions are shown.
524
- */
525
-
526
413
  var _this = this;
527
414
  this.settings = options.settings;
528
- this.$el.attr('data-cid', this.model.cid).addClass(this.model.get('extension'));
529
- this.model.bind('change:name', function(model, name) {
530
- var treeView;
531
- treeView = _this.settings.get('treeView');
532
- return treeView.render().trigger('rename', model, model.nameWithExtension());
533
- });
534
- return this.model.bind('change:extension', function(model, extension) {
535
- var treeView;
536
- _this.$el.attr('class', "file " + extension);
537
- treeView = _this.settings.get('treeView');
538
- return treeView.render().trigger('rename', model, model.nameWithExtension());
415
+ this.tree = options.tree;
416
+ return this.model.bind('change:path', function(model) {
417
+ return _this.render();
539
418
  });
540
419
  };
541
420
 
542
421
  File.prototype.render = function() {
543
- /*
544
- Internal: Sets the text of the file node according to the underlying model
545
- name. If the 'showExtensions' setting is set, renders the
546
- full file name with extension, otherwise renders just the file
547
- name attribute.
548
- */
549
- if (this.model.get('hidden')) {
550
- return "";
551
- }
422
+ var extension, name;
423
+ name = this.model.name();
424
+ extension = name.extension();
425
+ this.$el.attr('data-cid', this.model.cid);
426
+ this.$el.attr('class', "file " + extension);
552
427
  if (this.settings.get('showExtensions')) {
553
- this.$el.text(this.model.nameWithExtension());
428
+ this.$el.text(name);
554
429
  } else {
555
- this.$el.text(this.model.get('name'));
430
+ this.$el.text(name.withoutExtension());
556
431
  }
557
432
  return this;
558
433
  };
@@ -566,130 +441,7 @@
566
441
  (function() {
567
442
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
568
443
  __hasProp = {}.hasOwnProperty,
569
- __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; };
570
-
571
- BoneTree.namespace("BoneTree.Views", function(Views) {
572
- return Views.Menu = (function(_super) {
573
-
574
- __extends(Menu, _super);
575
-
576
- Menu.name = 'Menu';
577
-
578
- function Menu() {
579
- this.render = __bind(this.render, this);
580
-
581
- this.rename = __bind(this.rename, this);
582
-
583
- this["delete"] = __bind(this["delete"], this);
584
-
585
- this.contextMenu = __bind(this.contextMenu, this);
586
- return Menu.__super__.constructor.apply(this, arguments);
587
- }
588
-
589
- /*
590
- Internal: View that controls the context menu (class: 'filetree\_context\_menu').
591
-
592
- Events
593
-
594
- * contextMenu - Prevents the standard browser context menu from appearing
595
- when right clicking within the file tree context menu.
596
-
597
- * click .rename - Prompts the user to rename a file.
598
-
599
- * click .delete - Deletes a node from the file tree.
600
- */
601
-
602
-
603
- Menu.prototype.className = 'filetree_context_menu';
604
-
605
- Menu.prototype.events = {
606
- 'contextmenu': 'contextMenu',
607
- 'click .rename': 'rename',
608
- 'click .delete': 'delete'
609
- };
610
-
611
- Menu.prototype.initialize = function(options) {
612
- /*
613
- Internal: Initialize a new menu widget.
614
-
615
- * options - An Object. Internally used to pass the settings configuration
616
- into the menu. This controls whether or not the user is
617
- prompted to confirm deleting a file.
618
- */
619
- return this.settings = options.settings;
620
- };
621
-
622
- Menu.prototype.contextMenu = function(e) {
623
- /*
624
- Internal: Kill the default browser behavior for the contextmenu event.
625
- */
626
- e.preventDefault();
627
- return e.stopPropagation();
628
- };
629
-
630
- Menu.prototype["delete"] = function(e) {
631
- /*
632
- Internal: Deletes a node from the file tree. If the confirmDeletes setting
633
- is set, prompts the user for delete confirmation.
634
- */
635
- if (this.settings.get('confirmDeletes')) {
636
- if (confirm("Are you sure you want to delete '" + (this.model.nameWithExtension()) + "'?")) {
637
- this.model.destroy();
638
- }
639
- } else {
640
- this.model.destroy();
641
- }
642
- return this.$el.hide();
643
- };
644
-
645
- Menu.prototype.rename = function(e) {
646
- /*
647
- Internal: Prompts the user to rename a File or Directory.
648
- */
649
-
650
- var extension, fileName, newName, _ref;
651
- if (newName = prompt("New Name", this.model.nameWithExtension())) {
652
- _ref = newName.split("."), fileName = _ref[0], extension = _ref[1];
653
- if (extension == null) {
654
- extension = "";
655
- }
656
- this.model.set({
657
- name: fileName,
658
- extension: extension
659
- });
660
- }
661
- return this.$el.hide();
662
- };
663
-
664
- Menu.prototype.render = function() {
665
- /*
666
- Internal: Renders the <ul> that contains the context menu choices
667
- 'Rename' and 'Delete'.
668
-
669
- Returns `this`, the menu view.
670
- */
671
- this.$el.html(this.template());
672
- return this;
673
- };
674
-
675
- Menu.prototype.template = function() {
676
- /*
677
- Internal: html template for the context menu.
678
- */
679
- return "<ul>\n <li class='rename'>Rename</li>\n <hr/>\n <li class='delete'>Delete</li>\n</ul>";
680
- };
681
-
682
- return Menu;
683
-
684
- })(Backbone.View);
685
- });
686
-
687
- }).call(this);
688
- (function() {
689
- var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
690
- __hasProp = {}.hasOwnProperty,
691
- __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; },
692
- __slice = [].slice;
444
+ __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; };
693
445
 
694
446
  BoneTree.namespace("BoneTree.Views", function(Views) {
695
447
  var Models;
@@ -698,8 +450,6 @@
698
450
 
699
451
  __extends(Tree, _super);
700
452
 
701
- Tree.name = 'Tree';
702
-
703
453
  function Tree() {
704
454
  this.render = __bind(this.render, this);
705
455
 
@@ -707,78 +457,38 @@
707
457
 
708
458
  this._openDirectory = __bind(this._openDirectory, this);
709
459
 
710
- this.getModelFromClick = __bind(this.getModelFromClick, this);
460
+ this.getViewFromClick = __bind(this.getViewFromClick, this);
711
461
 
712
462
  this.toAscii = __bind(this.toAscii, this);
713
463
 
714
- this.files = __bind(this.files, this);
715
-
716
- this._getFile = __bind(this._getFile, this);
464
+ this.getFile = __bind(this.getFile, this);
717
465
 
718
466
  this.getDirectory = __bind(this.getDirectory, this);
719
467
 
720
- this.flatten = __bind(this.flatten, this);
721
-
722
- this.filterNodes = __bind(this.filterNodes, this);
723
-
724
- this._contextMenu = __bind(this._contextMenu, this);
725
-
726
- this._closeMenu = __bind(this._closeMenu, this);
727
-
728
- this.closeDirectories = __bind(this.closeDirectories, this);
729
-
730
- this.getModelByCid = __bind(this.getModelByCid, this);
468
+ this.toArray = __bind(this.toArray, this);
731
469
 
732
470
  this.findOrCreateView = __bind(this.findOrCreateView, this);
733
471
 
734
- this.addToTree = __bind(this.addToTree, this);
735
-
736
- this.addFromJSON = __bind(this.addFromJSON, this);
472
+ this.remove = __bind(this.remove, this);
737
473
 
738
- this.file = __bind(this.file, this);
474
+ this.add = __bind(this.add, this);
739
475
  return Tree.__super__.constructor.apply(this, arguments);
740
476
  }
741
477
 
742
- /*
743
- Public: The base tree object. Events from other objects are proxied to the tree
744
- so API consumers only need to know about this top level object.
745
- */
746
-
747
-
748
478
  Tree.prototype.className = 'tree';
749
479
 
750
480
  Tree.prototype.events = {
751
- 'contextmenu .file': '_contextMenu',
752
- 'contextmenu .directory': '_contextMenu',
753
- 'click .directory': '_openDirectory',
754
- 'click .file': '_openFile'
481
+ 'mousedown .directory': '_openDirectory',
482
+ 'mousedown .file': '_openFile'
755
483
  };
756
484
 
757
485
  Tree.prototype.initialize = function() {
758
- /*
759
- Public: Initialize a new filetree widget
760
-
761
- * options - An Object of global configuration options for the file tree.
762
- * confirmDeletes - A Boolean. If true, the tree will prompt the user, making
763
- sure they want to delete the file (default: false).
764
- * showExtensions - A Boolean. If true, files display their extensions. Internally,
765
- extensions are always kept track of but by default they are
766
- hidden (default: false).
767
- */
768
-
769
- var settingsConfig,
770
- _this = this;
771
- $(document).click(this._closeMenu);
772
- this._currentFileData = null;
773
- settingsConfig = _.extend({}, this.options, {
774
- treeView: this
775
- });
776
- this.settings = new Models.Settings(settingsConfig);
777
- this.menuView = new Views.Menu({
778
- settings: this.settings
486
+ var _this = this;
487
+ this.viewCache = {};
488
+ this.settings = new Models.Settings(this.options);
489
+ this.root = new Models.Directory({
490
+ path: '/'
779
491
  });
780
- this.menuView.render().$el.appendTo($('body'));
781
- this.root = new Models.Node;
782
492
  this.root.collection.bind('add', this.render);
783
493
  return this.root.collection.bind('remove', function(model, collection) {
784
494
  _this.$("[data-cid='" + model.cid + "']").remove();
@@ -787,497 +497,83 @@
787
497
  });
788
498
  };
789
499
 
790
- Tree.prototype.file = function(filePath, fileData) {
791
- var dirs, file, fileName, _i, _ref;
792
- if (filePath[0] === '/') {
793
- filePath = filePath.replace('/', '');
794
- }
795
- if (fileData != null) {
796
- this._currentFileData = _.extend(fileData, {
797
- path: filePath
798
- });
799
- if (this._currentFileData.autoOpen == null) {
800
- this._currentFileData.autoOpen = true;
801
- }
802
- if (this._currentFileData.hidden == null) {
803
- this._currentFileData.hidden = false;
500
+ Tree.prototype.add = function(filePath, fileData) {
501
+ var file, _i, _len, _results;
502
+ if (Object.isArray(filePath)) {
503
+ _results = [];
504
+ for (_i = 0, _len = filePath.length; _i < _len; _i++) {
505
+ file = filePath[_i];
506
+ _results.push(this.add(file.path, file));
804
507
  }
508
+ return _results;
805
509
  } else {
806
- return this._getFile(filePath);
807
- }
808
- _ref = filePath.split('/'), dirs = 2 <= _ref.length ? __slice.call(_ref, 0, _i = _ref.length - 1) : (_i = 0, []), fileName = _ref[_i++];
809
- if (file = this._getFile(filePath)) {
810
- return file.set(this._currentFileData);
811
- } else {
812
- return this.addToTree(this.root, dirs, fileName);
510
+ return this.root.add(filePath, fileData);
813
511
  }
814
512
  };
815
513
 
816
- Tree.prototype.addFromJSON = function(data, currentPath) {
817
- var file, name, _i, _len, _ref;
818
- if (currentPath == null) {
819
- currentPath = "";
820
- }
821
- /*
822
- Public: Creates a file tree from a JSON representation. Expects the
823
- JSON object to have a `name` property at each level, specifying
824
- the name of the file or directory, and a files array if the
825
- current node has child directories or files.
826
-
827
- * data - An Object that represents hierarchical file data.
828
-
829
- * currentPath - A String representing the current location in the tree.
830
- Defaults to the file tree root. (default: "")
831
-
832
- Examples
833
-
834
- data = {
835
- name: "My Project"
836
- files: [
837
- { name: "Empty Folder" }
838
- { name: "SomeFile.coffee" }
839
- { name: "AnotherFile.coffee" }
840
- {
841
- name: "Folder with Files inside"
842
- files: [
843
- { name: "NestedFile.coffee" }
844
- ]
845
- }
846
- ]
847
- }
848
-
849
- tree.addFromJSON(data)
850
- # => <Tree>
851
-
852
- Returns the Tree view object.
853
- */
854
-
855
- name = "";
856
- if (data.name != null) {
857
- name = data.name + '/';
858
- delete data.name;
859
- }
860
- if (data.extension != null) {
861
- name = name.replace('/', '.' + data.extension);
862
- delete data.extension;
863
- }
864
- currentPath += name;
865
- if (data.files != null) {
866
- _ref = data.files;
867
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
868
- file = _ref[_i];
869
- this.addFromJSON(file, currentPath);
870
- }
871
- } else {
872
- this.file(currentPath, data);
873
- }
874
- return this;
514
+ Tree.prototype.remove = function(path) {
515
+ return this.root.remove(path);
875
516
  };
876
517
 
877
- Tree.prototype.addToTree = function(currentDirectory, remainingDirectories, fileName) {
878
- /*
879
- Internal: Recursive method that traverses nodes, creating
880
- Files and Directories.
881
-
882
- * currentDirectory - A Node object representing which directory we are
883
- adding the current Directory or File to.
884
- * remainingDirectories - A '/' separated String representing the remaining
885
- directories to add.
886
- * fileName - The String name of the file to be added. This can
887
- include the extension name separated by a '.'.
888
-
889
- Examples
890
-
891
- tree.addToTree(@root, '/source/subdirectory/', 'main.coffee')
892
- # => <File>
893
-
894
- Returns the File object if it was created and null if no file was given.
895
- */
896
-
897
- var file, matchingDirectory, newDirectory, newNode, nextDirectoryName;
898
- if (remainingDirectories.length) {
899
- nextDirectoryName = remainingDirectories.shift();
900
- if (matchingDirectory = Models.Directory.find(currentDirectory, nextDirectoryName)) {
901
- matchingDirectory.set({
902
- open: true
903
- });
904
- return this.addToTree(matchingDirectory, remainingDirectories, fileName);
905
- } else {
906
- newNode = new Models.Directory({
907
- name: nextDirectoryName,
908
- open: true
909
- });
910
- newDirectory = currentDirectory.collection.add(newNode);
911
- return this.addToTree(newNode, remainingDirectories, fileName);
912
- }
518
+ Tree.prototype.findOrCreateView = function(node) {
519
+ var type, view;
520
+ if (node.isDirectory()) {
521
+ type = 'Directory';
913
522
  } else {
914
- if (fileName === "") {
915
- return null;
916
- }
917
- file = Models.File.createFromFileName(fileName, this._currentFileData);
918
- this._currentFileData = null;
919
- currentDirectory.collection.add(file);
920
- if (file.get('autoOpen')) {
921
- this.trigger('openFile', file);
922
- }
923
- return file;
523
+ type = 'File';
924
524
  }
925
- };
926
-
927
- Tree.prototype.findOrCreateView = function(node) {
928
- /*
929
- Internal: Look up existing view in the view cache or Create a new view
930
- of the correct type (either File or Directory).
931
-
932
- * node - A Node object. Either a File object or a Directory object.
933
- This is the model that the view will be associated with.
934
-
935
- Examples
936
-
937
- file = new BoneTree.Models.File
938
-
939
- # This will create a new view since we just created the File
940
- tree.findOrCreateView(file)
941
- # => <FileView>
942
-
943
- Returns the view corresponding to the model passed in.
944
- */
945
-
946
- var type, view, viewCache;
947
- type = node.constantize();
948
- viewCache = this.settings.get('viewCache');
949
- if (!(view = viewCache[node.cid])) {
950
- view = viewCache[node.cid] = new Views[type]({
525
+ if (!(view = this.viewCache[node.cid])) {
526
+ view = this.viewCache[node.cid] = new Views[type]({
951
527
  model: node,
952
- settings: this.settings
528
+ settings: this.settings,
529
+ tree: this
953
530
  });
954
531
  }
955
532
  return view;
956
533
  };
957
534
 
958
- Tree.prototype.getModelByCid = function(cid) {
959
- var modelCid, view, viewCache;
960
- viewCache = this.settings.get('viewCache');
961
- for (modelCid in viewCache) {
962
- view = viewCache[modelCid];
963
- if (modelCid === cid) {
964
- return view.model;
965
- }
966
- }
535
+ Tree.prototype.toArray = function() {
536
+ return this.root.toArray();
967
537
  };
968
538
 
969
- Tree.prototype.closeDirectories = function() {
970
- /*
971
- Public: Close all the directories in the file tree.
972
-
973
- Examples
974
-
975
- tree.closeDirectories()
976
- # => <Tree>
977
-
978
- Returns the Tree view object.
979
- */
980
-
981
- var directories;
982
- directories = _.filter(this.flatten(), function(node) {
983
- return node.get('nodeType') === 'directory';
984
- });
985
- _.invoke(directories, 'set', {
986
- open: false
987
- });
988
- return this;
539
+ Tree.prototype.getDirectory = function(directoryPath) {
540
+ return this.root.getDirectory(directoryPath);
989
541
  };
990
542
 
991
- Tree.prototype._closeMenu = function(e) {
992
- /*
993
- Internal: Close the context menu. This is called every click on
994
- the document and closes the menu unless you are clicking
995
- within it. This shouldn't be called directly, it is called
996
- automatically by Backbone from user interactions.
997
-
998
- Returns the Menu view object.
999
- */
1000
- if (!$(e.currentTarget).is('.menu')) {
1001
- this.menuView.$el.hide();
1002
- }
1003
- return this.menuView;
543
+ Tree.prototype.getFile = function(filePath) {
544
+ return this.root.getFile(filePath);
1004
545
  };
1005
546
 
1006
- Tree.prototype._contextMenu = function(e) {
1007
- /*
1008
- Internal: Open the context menu. This prevents the default browser
1009
- context menu event. This shouldn't be called directly, it is
1010
- called automatically by Backbone from user interations.
1011
-
1012
- Returns the Menu view object.
1013
- */
1014
-
1015
- var model;
1016
- e.preventDefault();
1017
- model = this.getModelFromClick(e);
1018
- this.menuView.model = model;
1019
- this.menuView.$el.css({
1020
- left: e.pageX,
1021
- top: e.pageY
1022
- }).show();
1023
- return this.menuView;
547
+ Tree.prototype.toAscii = function() {
548
+ return '\n' + this.root.toAscii();
1024
549
  };
1025
550
 
1026
- Tree.prototype.filterNodes = function(nodeType, nodeName) {
1027
- /*
1028
- Internal: Returns file tree nodes that match the nodeType and nodeName.
1029
-
1030
- * nodeType - A String that represents the nodeType to match. Choices are
1031
- 'file' or 'directory'.
1032
- * nodeName - A String that represents the name of the node to match.
1033
-
1034
- Examples
1035
-
1036
- # Add some files to the tree
1037
- tree.file('/source/main.coffee')
1038
- tree.file('/source/player.coffee')
1039
-
1040
- # returns an array containing the File 'main.coffee'
1041
- tree.filterNodes('file', 'main')
1042
- # => [<File>]
1043
-
1044
- Returns an Array of nodes that match the filter criteria.
1045
- */
1046
-
1047
- var results,
1048
- _this = this;
1049
- results = _.filter(this.flatten(), function(node) {
1050
- return node.get('nodeType') === nodeType && node.get('name') === nodeName;
1051
- });
1052
- return results;
1053
- };
1054
-
1055
- Tree.prototype.flatten = function(currentNode, results) {
1056
- var _this = this;
1057
- if (currentNode == null) {
1058
- currentNode = this.root;
1059
- }
1060
- if (results == null) {
1061
- results = [];
1062
- }
1063
- /*
1064
- Internal: Returns a one dimensional ordered array representing the
1065
- Directory and File nodes in the tree.
1066
-
1067
- * currentNode - The node to start at when flattening
1068
- * nodeName - A String that represents the name of the node to match.
1069
-
1070
- Examples
1071
-
1072
- # Add some files to the tree
1073
- tree.file('/source/main.coffee', {aFile: true})
1074
- tree.file('/source/player.coffee', {playerData: {x: 50, y: 30}})
1075
-
1076
- # returns an array containing the File 'main.coffee'
1077
- tree.filterNodes('file', 'main')
1078
- # => [<File>]
1079
-
1080
- Returns an Array of nodes that match the filter criteria.
1081
- */
1082
-
1083
- currentNode.collection.each(function(node) {
1084
- results.push(node);
1085
- if (node.collection.length) {
1086
- return _this.flatten(node, results);
1087
- }
1088
- });
1089
- return results;
1090
- };
1091
-
1092
- Tree.prototype.getDirectory = function(directoryName) {
1093
- /*
1094
- Public: Returns an array of directories matching the given directoryName.
1095
-
1096
- * directoryName - A String naming the directory to match.
1097
-
1098
- Examples
1099
-
1100
- # Add some files to the tree
1101
- tree.file('/source/main.coffee', {size: 4039})
1102
- tree.file('/source/player.coffee', {size: 399})
1103
- tree.file('/directory2/file.coffee', {size: 23})
1104
-
1105
- # returns an array containing the Directory 'source'
1106
- tree.getDirectory('source')
1107
- # => [<Directory>]
1108
-
1109
- Returns an Array of Directory nodes that match directoryName.
1110
- */
1111
- return this.filterNodes('directory', directoryName);
1112
- };
1113
-
1114
- Tree.prototype._getFile = function(filePath) {
1115
- /*
1116
- Internal: Returns a file at the specified location.
1117
-
1118
- * fileName - A String describing the file path.
1119
-
1120
- Examples
1121
-
1122
- # Add some files to the tree
1123
- tree.file('/source/main.coffee', {size: 30459})
1124
- tree.file('/source/player.coffee', {size: 943})
1125
- tree.file('/directory2/main.coffee', {size: 4945})
1126
-
1127
- # returns an array containing both the files named main.
1128
- tree._getFile('source/main.coffee')
1129
- # => <File>
1130
-
1131
- Returns a File at the given location.
1132
- */
1133
-
1134
- var filtered, nodes;
1135
- if (filePath[0] === '/') {
1136
- filePath = filePath.replace('/', '');
1137
- }
1138
- nodes = this.flatten();
1139
- filtered = _.filter(nodes, function(node) {
1140
- return node.get('nodeType') === 'file' && node.get('path') === filePath;
1141
- });
1142
- return filtered[0];
1143
- };
1144
-
1145
- Tree.prototype.files = function(directoryName) {
1146
- /*
1147
- Public: Returns an array of files contained within the directory
1148
- matching directoryName.
1149
-
1150
- * directoryName - A String naming the directory to look inside.
1151
-
1152
- Examples
1153
-
1154
- # Add some files to the tree
1155
- tree.file('/source/main.coffee', {main: true})
1156
- tree.file('/source/player.coffee', {active: true})
1157
- tree.file('/directory2/main.coffee', {active: true})
1158
-
1159
- # returns an array containing the files 'player.coffee' and 'main.coffee'
1160
- tree.files('source')
1161
- # => [<File>, <File>]
1162
-
1163
- Returns an Array of File nodes that are contained in the
1164
- Directory matching directoryName.
1165
- */
1166
-
1167
- var directory, nodesInDirectory;
1168
- if (directoryName == null) {
1169
- return _.filter(this.flatten(), function(node) {
1170
- return node.get('nodeType') === 'file';
1171
- });
1172
- }
1173
- directory = this.getDirectory(directoryName)[0];
1174
- if (!directory) {
1175
- return [];
1176
- }
1177
- nodesInDirectory = this.flatten(directory);
1178
- return _.filter(nodesInDirectory, function(node) {
1179
- return node.get('nodeType') === 'file';
1180
- });
1181
- };
1182
-
1183
- Tree.prototype.toAscii = function(collection, indentation, output) {
1184
- var n, rootCollection, spaces, _i,
1185
- _this = this;
1186
- if (indentation == null) {
1187
- indentation = 0;
1188
- }
1189
- if (output == null) {
1190
- output = "\n";
1191
- }
1192
- /*
1193
- Internal: A String representation of the filetree.
1194
-
1195
- * collection - A NodeCollection object describing which folder to start at.
1196
- * indentation - A Number describing how many spaces to indent the next filetree element (default: 0).
1197
- * output - A String representing the current filetree output (default: "\n").
1198
-
1199
- Examples
1200
-
1201
- # Add some files to the tree
1202
- tree.file('/source/main.coffee', {main: true})
1203
- tree.file('/source/player.coffee', {active: true})
1204
- tree.file('/directory2/main.coffee', {active: false})
1205
-
1206
- tree.toAscii()
1207
- # => "
1208
- -directory2
1209
- -main.coffee
1210
- -source
1211
- -main.coffee
1212
- -player.coffee
1213
- "
1214
-
1215
- Returns a String representation of the sorted nodes of the file tree.
1216
- */
1217
-
1218
- rootCollection = collection || this.root.collection;
1219
- spaces = "";
1220
- for (n = _i = 0; 0 <= indentation ? _i <= indentation : _i >= indentation; n = 0 <= indentation ? ++_i : --_i) {
1221
- spaces += " ";
1222
- }
1223
- rootCollection.each(function(nodes) {
1224
- var typeChar;
1225
- typeChar = nodes.get('type') === 'directory' ? '+' : '-';
1226
- output += spaces + typeChar + nodes.nameWithExtension() + '\n';
1227
- return output = _this.toAscii(nodes.collection, indentation + 1, output);
1228
- });
1229
- return output;
1230
- };
1231
-
1232
- Tree.prototype.getModelFromClick = function(e) {
1233
- /*
1234
- Internal: Look up a model based on the cid of the clicked view element.
1235
-
1236
- Returns the Node corresponding to the view element that the user clicked on.
1237
- */
1238
-
551
+ Tree.prototype.getViewFromClick = function(e) {
1239
552
  var cid;
1240
553
  e.stopPropagation();
1241
- this.menuView.$el.hide();
1242
554
  cid = $(e.currentTarget).data('cid');
1243
- return this.getModelByCid(cid);
555
+ return this.viewCache[cid];
1244
556
  };
1245
557
 
1246
558
  Tree.prototype._openDirectory = function(e) {
1247
- /*
1248
- Internal: Toggle the directory icon and display the contents of the clicked Directory.
1249
- */
1250
-
1251
- var model;
1252
- model = this.getModelFromClick(e);
1253
- return model.toggleOpen();
559
+ var view;
560
+ e.stopPropagation();
561
+ view = this.getViewFromClick(e);
562
+ return view.toggleOpen();
1254
563
  };
1255
564
 
1256
565
  Tree.prototype._openFile = function(e) {
1257
- /*
1258
- Internal: Trigger the 'openFile' event, passing in the file corresponding
1259
- to the view element that the user clicked.
1260
- */
1261
-
1262
- var model;
1263
- model = this.getModelFromClick(e);
1264
- return this.trigger('openFile', model);
566
+ var view;
567
+ view = this.getViewFromClick(e);
568
+ return this.trigger('openFile', view.model);
1265
569
  };
1266
570
 
1267
571
  Tree.prototype.render = function() {
1268
- /*
1269
- Internal: Call render on each of the nodes underneath the root node.
1270
- Also calls sort on each of the subcollections.
1271
- */
1272
-
1273
572
  var _this = this;
1274
- this.root.collection.sort().each(function(node) {
573
+ this.root.collection.each(function(node) {
1275
574
  var view;
1276
- node.collection.sort();
1277
575
  view = _this.findOrCreateView(node);
1278
- if (!view.model.get('hidden')) {
1279
- return _this.$el.append(view.render().$el);
1280
- }
576
+ return _this.$el.append(view.render().$el);
1281
577
  });
1282
578
  return this;
1283
579
  };