bone_tree 0.5.6 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  };