bone_tree 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ task :build => [:spec] do
7
7
  # Build static assets
8
8
  `middleman build`
9
9
 
10
- `cp build/javascripts/bone_tree.js lib/assets/src/bone_tree.js`
10
+ `cp build/javascripts/bone_tree.js lib/assets/javascripts/bone_tree.js`
11
11
  `gem build bone_tree.gemspec`
12
12
  end
13
13
 
@@ -0,0 +1,1292 @@
1
+ (function() {
2
+ var __slice = [].slice;
3
+
4
+ window.BoneTree = {};
5
+
6
+ BoneTree.namespace = function(target, name, block) {
7
+ var item, top, _i, _len, _ref, _ref1;
8
+ if (arguments.length < 3) {
9
+ _ref = [(typeof exports !== 'undefined' ? exports : window)].concat(__slice.call(arguments)), target = _ref[0], name = _ref[1], block = _ref[2];
10
+ }
11
+ top = target;
12
+ _ref1 = name.split('.');
13
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
14
+ item = _ref1[_i];
15
+ target = target[item] || (target[item] = {});
16
+ }
17
+ return block(target, top);
18
+ };
19
+
20
+ }).call(this);
21
+ (function() {
22
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
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; };
25
+
26
+ BoneTree.namespace("BoneTree.Models", function(Models) {
27
+ Models.Node = (function(_super) {
28
+
29
+ __extends(Node, _super);
30
+
31
+ Node.name = 'Node';
32
+
33
+ function Node() {
34
+ this.nameWithExtension = __bind(this.nameWithExtension, this);
35
+
36
+ this.constantize = __bind(this.constantize, this);
37
+ return Node.__super__.constructor.apply(this, arguments);
38
+ }
39
+
40
+ /*
41
+ Internal: An abstract super class for File and Directory objects to inherit from.
42
+ */
43
+
44
+
45
+ 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
+ return this.collection = new Models.Nodes;
51
+ };
52
+
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);
78
+ };
79
+
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;
117
+ };
118
+
119
+ return Node;
120
+
121
+ })(Backbone.Model);
122
+ return Models.Nodes = (function(_super) {
123
+
124
+ __extends(Nodes, _super);
125
+
126
+ Nodes.name = 'Nodes';
127
+
128
+ function Nodes() {
129
+ return Nodes.__super__.constructor.apply(this, arguments);
130
+ }
131
+
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
+ 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;
167
+ };
168
+
169
+ Nodes.prototype.model = Models.Node;
170
+
171
+ return Nodes;
172
+
173
+ })(Backbone.Collection);
174
+ });
175
+
176
+ }).call(this);
177
+ (function() {
178
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
179
+ __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; };
181
+
182
+ BoneTree.namespace("BoneTree.Models", function(Models) {
183
+ Models.Directory = (function(_super) {
184
+
185
+ __extends(Directory, _super);
186
+
187
+ Directory.name = 'Directory';
188
+
189
+ function Directory() {
190
+ this.toggleOpen = __bind(this.toggleOpen, this);
191
+ return Directory.__super__.constructor.apply(this, arguments);
192
+ }
193
+
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"
216
+ };
217
+
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
240
+ });
241
+ };
242
+
243
+ return Directory;
244
+
245
+ })(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
+ });
263
+
264
+ }).call(this);
265
+ (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;
269
+
270
+ BoneTree.namespace("BoneTree.Models", function(Models) {
271
+ Models.File = (function(_super) {
272
+
273
+ __extends(File, _super);
274
+
275
+ File.name = 'File';
276
+
277
+ function File() {
278
+ return File.__super__.constructor.apply(this, arguments);
279
+ }
280
+
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"
299
+ };
300
+
301
+ return File;
302
+
303
+ })(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
+ });
339
+
340
+ }).call(this);
341
+ (function() {
342
+ 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; };
344
+
345
+ BoneTree.namespace("BoneTree.Models", function(Models) {
346
+ return Models.Settings = (function(_super) {
347
+
348
+ __extends(Settings, _super);
349
+
350
+ Settings.name = 'Settings';
351
+
352
+ function Settings() {
353
+ return Settings.__super__.constructor.apply(this, arguments);
354
+ }
355
+
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
+ Settings.prototype.defaults = {
377
+ confirmDeletes: false,
378
+ showExtensions: false,
379
+ viewCache: {}
380
+ };
381
+
382
+ return Settings;
383
+
384
+ })(Backbone.Model);
385
+ });
386
+
387
+ }).call(this);
388
+ (function() {
389
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
390
+ __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; };
392
+
393
+ BoneTree.namespace("BoneTree.Views", function(Views) {
394
+ return Views.Directory = (function(_super) {
395
+
396
+ __extends(Directory, _super);
397
+
398
+ Directory.name = 'Directory';
399
+
400
+ function Directory() {
401
+ this.displayChildren = __bind(this.displayChildren, this);
402
+
403
+ this.render = __bind(this.render, this);
404
+
405
+ this.appendView = __bind(this.appendView, this);
406
+ return Directory.__super__.constructor.apply(this, arguments);
407
+ }
408
+
409
+ /*
410
+ Internal: View that renders a Directory node and controls its behavior (class: 'directory', tag: 'ul').
411
+ */
412
+
413
+
414
+ Directory.prototype.className = 'directory';
415
+
416
+ Directory.prototype.tagName = 'ul';
417
+
418
+ 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
+ var _this = this;
430
+ this.settings = options.settings;
431
+ 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);
439
+ });
440
+ this.model.collection.bind('add', this.render);
441
+ this.model.collection.bind('remove', function(model, collection) {
442
+ _this.settings.get('treeView').trigger('remove', model);
443
+ return _this.render();
444
+ });
445
+ return this.displayChildren(this.model.get('open'));
446
+ };
447
+
448
+ 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
+ var view;
457
+ view = this.settings.get('treeView').findOrCreateView(node);
458
+ return this.$el.append(view.render().$el);
459
+ };
460
+
461
+ 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);
469
+ return this;
470
+ };
471
+
472
+ Directory.prototype.displayChildren = function(open) {
473
+ /*
474
+ Internal: Toggles display of the children Files or Diretories of this view.
475
+ */
476
+
477
+ var fileDirChildren;
478
+ fileDirChildren = this.$el.children('.directory, .file');
479
+ this.$el.toggleClass('open', open);
480
+ return fileDirChildren.toggle(open);
481
+ };
482
+
483
+ return Directory;
484
+
485
+ })(Backbone.View);
486
+ });
487
+
488
+ }).call(this);
489
+ (function() {
490
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
491
+ __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; };
493
+
494
+ BoneTree.namespace("BoneTree.Views", function(Views) {
495
+ return Views.File = (function(_super) {
496
+
497
+ __extends(File, _super);
498
+
499
+ File.name = 'File';
500
+
501
+ function File() {
502
+ this.render = __bind(this.render, this);
503
+ return File.__super__.constructor.apply(this, arguments);
504
+ }
505
+
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
+ File.prototype.tagName = 'li';
514
+
515
+ 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
+ var _this = this;
527
+ 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());
539
+ });
540
+ };
541
+
542
+ 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.settings.get('showExtensions')) {
550
+ this.$el.text(this.model.nameWithExtension());
551
+ } else {
552
+ this.$el.text(this.model.get('name'));
553
+ }
554
+ return this;
555
+ };
556
+
557
+ return File;
558
+
559
+ })(Backbone.View);
560
+ });
561
+
562
+ }).call(this);
563
+ (function() {
564
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
565
+ __hasProp = {}.hasOwnProperty,
566
+ __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; };
567
+
568
+ BoneTree.namespace("BoneTree.Views", function(Views) {
569
+ return Views.Menu = (function(_super) {
570
+
571
+ __extends(Menu, _super);
572
+
573
+ Menu.name = 'Menu';
574
+
575
+ function Menu() {
576
+ this.render = __bind(this.render, this);
577
+
578
+ this.rename = __bind(this.rename, this);
579
+
580
+ this["delete"] = __bind(this["delete"], this);
581
+
582
+ this.contextMenu = __bind(this.contextMenu, this);
583
+ return Menu.__super__.constructor.apply(this, arguments);
584
+ }
585
+
586
+ /*
587
+ Internal: View that controls the context menu (class: 'filetree\_context\_menu').
588
+
589
+ Events
590
+
591
+ * contextMenu - Prevents the standard browser context menu from appearing
592
+ when right clicking within the file tree context menu.
593
+
594
+ * click .rename - Prompts the user to rename a file.
595
+
596
+ * click .delete - Deletes a node from the file tree.
597
+ */
598
+
599
+
600
+ Menu.prototype.className = 'filetree_context_menu';
601
+
602
+ Menu.prototype.events = {
603
+ 'contextmenu': 'contextMenu',
604
+ 'click .rename': 'rename',
605
+ 'click .delete': 'delete'
606
+ };
607
+
608
+ Menu.prototype.initialize = function(options) {
609
+ /*
610
+ Internal: Initialize a new menu widget.
611
+
612
+ * options - An Object. Internally used to pass the settings configuration
613
+ into the menu. This controls whether or not the user is
614
+ prompted to confirm deleting a file.
615
+ */
616
+ return this.settings = options.settings;
617
+ };
618
+
619
+ Menu.prototype.contextMenu = function(e) {
620
+ /*
621
+ Internal: Kill the default browser behavior for the contextmenu event.
622
+ */
623
+ e.preventDefault();
624
+ return e.stopPropagation();
625
+ };
626
+
627
+ Menu.prototype["delete"] = function(e) {
628
+ /*
629
+ Internal: Deletes a node from the file tree. If the confirmDeletes setting
630
+ is set, prompts the user for delete confirmation.
631
+ */
632
+ if (this.settings.get('confirmDeletes')) {
633
+ if (confirm("Are you sure you want to delete '" + (this.model.nameWithExtension()) + "'?")) {
634
+ this.model.destroy();
635
+ }
636
+ } else {
637
+ this.model.destroy();
638
+ }
639
+ return this.$el.hide();
640
+ };
641
+
642
+ Menu.prototype.rename = function(e) {
643
+ /*
644
+ Internal: Prompts the user to rename a File or Directory.
645
+ */
646
+
647
+ var extension, fileName, newName, _ref;
648
+ if (newName = prompt("New Name", this.model.nameWithExtension())) {
649
+ _ref = newName.split("."), fileName = _ref[0], extension = _ref[1];
650
+ if (extension == null) {
651
+ extension = "";
652
+ }
653
+ this.model.set({
654
+ name: fileName,
655
+ extension: extension
656
+ });
657
+ }
658
+ return this.$el.hide();
659
+ };
660
+
661
+ Menu.prototype.render = function() {
662
+ /*
663
+ Internal: Renders the <ul> that contains the context menu choices
664
+ 'Rename' and 'Delete'.
665
+
666
+ Returns `this`, the menu view.
667
+ */
668
+ this.$el.html(this.template());
669
+ return this;
670
+ };
671
+
672
+ Menu.prototype.template = function() {
673
+ /*
674
+ Internal: html template for the context menu.
675
+ */
676
+ return "<ul>\n <li class='rename'>Rename</li>\n <hr/>\n <li class='delete'>Delete</li>\n</ul>";
677
+ };
678
+
679
+ return Menu;
680
+
681
+ })(Backbone.View);
682
+ });
683
+
684
+ }).call(this);
685
+ (function() {
686
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
687
+ __hasProp = {}.hasOwnProperty,
688
+ __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; },
689
+ __slice = [].slice;
690
+
691
+ BoneTree.namespace("BoneTree.Views", function(Views) {
692
+ var Models;
693
+ Models = BoneTree.Models;
694
+ return Views.Tree = (function(_super) {
695
+
696
+ __extends(Tree, _super);
697
+
698
+ Tree.name = 'Tree';
699
+
700
+ function Tree() {
701
+ this.render = __bind(this.render, this);
702
+
703
+ this._openFile = __bind(this._openFile, this);
704
+
705
+ this._openDirectory = __bind(this._openDirectory, this);
706
+
707
+ this.getModelFromClick = __bind(this.getModelFromClick, this);
708
+
709
+ this.toAscii = __bind(this.toAscii, this);
710
+
711
+ this.files = __bind(this.files, this);
712
+
713
+ this._getFile = __bind(this._getFile, this);
714
+
715
+ this.getDirectory = __bind(this.getDirectory, this);
716
+
717
+ this.flatten = __bind(this.flatten, this);
718
+
719
+ this.filterNodes = __bind(this.filterNodes, this);
720
+
721
+ this._contextMenu = __bind(this._contextMenu, this);
722
+
723
+ this._closeMenu = __bind(this._closeMenu, this);
724
+
725
+ this.closeDirectories = __bind(this.closeDirectories, this);
726
+
727
+ this.getModelByCid = __bind(this.getModelByCid, this);
728
+
729
+ this.findOrCreateView = __bind(this.findOrCreateView, this);
730
+
731
+ this.addToTree = __bind(this.addToTree, this);
732
+
733
+ this.addFromJSON = __bind(this.addFromJSON, this);
734
+
735
+ this.file = __bind(this.file, this);
736
+ return Tree.__super__.constructor.apply(this, arguments);
737
+ }
738
+
739
+ /*
740
+ Public: The base tree object. Events from other objects are proxied to the tree
741
+ so API consumers only need to know about this top level object.
742
+ */
743
+
744
+
745
+ Tree.prototype.className = 'tree';
746
+
747
+ Tree.prototype.events = {
748
+ 'contextmenu .file': '_contextMenu',
749
+ 'contextmenu .directory': '_contextMenu',
750
+ 'click .directory': '_openDirectory',
751
+ 'click .file': '_openFile'
752
+ };
753
+
754
+ Tree.prototype.initialize = function() {
755
+ /*
756
+ Public: Initialize a new filetree widget
757
+
758
+ * options - An Object of global configuration options for the file tree.
759
+ * confirmDeletes - A Boolean. If true, the tree will prompt the user, making
760
+ sure they want to delete the file (default: false).
761
+ * showExtensions - A Boolean. If true, files display their extensions. Internally,
762
+ extensions are always kept track of but by default they are
763
+ hidden (default: false).
764
+ */
765
+
766
+ var settingsConfig,
767
+ _this = this;
768
+ $(document).click(this._closeMenu);
769
+ this._currentFileData = null;
770
+ settingsConfig = _.extend({}, this.options, {
771
+ treeView: this
772
+ });
773
+ this.settings = new Models.Settings(settingsConfig);
774
+ this.menuView = new Views.Menu({
775
+ settings: this.settings
776
+ });
777
+ this.menuView.render().$el.appendTo($('body'));
778
+ this.root = new Models.Node;
779
+ this.root.collection.bind('add', this.render);
780
+ return this.root.collection.bind('remove', function(model, collection) {
781
+ _this.$("[data-cid='" + model.cid + "']").remove();
782
+ _this.render();
783
+ return _this.trigger('remove', model);
784
+ });
785
+ };
786
+
787
+ Tree.prototype.file = function(filePath, fileData) {
788
+ var dirs, file, fileName, _i, _ref;
789
+ if (filePath[0] === '/') {
790
+ filePath = filePath.replace('/', '');
791
+ }
792
+ if (fileData != null) {
793
+ this._currentFileData = _.extend(fileData, {
794
+ path: filePath
795
+ });
796
+ if (this._currentFileData.autoOpen == null) {
797
+ this._currentFileData.autoOpen = true;
798
+ }
799
+ if (this._currentFileData.hidden == null) {
800
+ this._currentFileData.hidden = false;
801
+ }
802
+ } else {
803
+ return this._getFile(filePath);
804
+ }
805
+ _ref = filePath.split('/'), dirs = 2 <= _ref.length ? __slice.call(_ref, 0, _i = _ref.length - 1) : (_i = 0, []), fileName = _ref[_i++];
806
+ if (file = this._getFile(filePath)) {
807
+ return file.set(this._currentFileData);
808
+ } else {
809
+ return this.addToTree(this.root, dirs, fileName);
810
+ }
811
+ };
812
+
813
+ Tree.prototype.addFromJSON = function(data, currentPath) {
814
+ var file, name, _i, _len, _ref;
815
+ if (currentPath == null) {
816
+ currentPath = "";
817
+ }
818
+ /*
819
+ Public: Creates a file tree from a JSON representation. Expects the
820
+ JSON object to have a `name` property at each level, specifying
821
+ the name of the file or directory, and a files array if the
822
+ current node has child directories or files.
823
+
824
+ * data - An Object that represents hierarchical file data.
825
+
826
+ * currentPath - A String representing the current location in the tree.
827
+ Defaults to the file tree root. (default: "")
828
+
829
+ Examples
830
+
831
+ data = {
832
+ name: "My Project"
833
+ files: [
834
+ { name: "Empty Folder" }
835
+ { name: "SomeFile.coffee" }
836
+ { name: "AnotherFile.coffee" }
837
+ {
838
+ name: "Folder with Files inside"
839
+ files: [
840
+ { name: "NestedFile.coffee" }
841
+ ]
842
+ }
843
+ ]
844
+ }
845
+
846
+ tree.addFromJSON(data)
847
+ # => <Tree>
848
+
849
+ Returns the Tree view object.
850
+ */
851
+
852
+ name = "";
853
+ if (data.name != null) {
854
+ name = data.name + '/';
855
+ delete data.name;
856
+ }
857
+ if (data.extension != null) {
858
+ name = name.replace('/', '.' + data.extension);
859
+ delete data.extension;
860
+ }
861
+ currentPath += name;
862
+ if (data.files != null) {
863
+ _ref = data.files;
864
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
865
+ file = _ref[_i];
866
+ this.addFromJSON(file, currentPath);
867
+ }
868
+ } else {
869
+ this.file(currentPath, data);
870
+ }
871
+ return this;
872
+ };
873
+
874
+ Tree.prototype.addToTree = function(currentDirectory, remainingDirectories, fileName) {
875
+ /*
876
+ Internal: Recursive method that traverses nodes, creating
877
+ Files and Directories.
878
+
879
+ * currentDirectory - A Node object representing which directory we are
880
+ adding the current Directory or File to.
881
+ * remainingDirectories - A '/' separated String representing the remaining
882
+ directories to add.
883
+ * fileName - The String name of the file to be added. This can
884
+ include the extension name separated by a '.'.
885
+
886
+ Examples
887
+
888
+ tree.addToTree(@root, '/source/subdirectory/', 'main.coffee')
889
+ # => <File>
890
+
891
+ Returns the File object if it was created and null if no file was given.
892
+ */
893
+
894
+ var file, matchingDirectory, newDirectory, newNode, nextDirectoryName;
895
+ if (remainingDirectories.length) {
896
+ nextDirectoryName = remainingDirectories.shift();
897
+ if (matchingDirectory = Models.Directory.find(currentDirectory, nextDirectoryName)) {
898
+ matchingDirectory.set({
899
+ open: true
900
+ });
901
+ return this.addToTree(matchingDirectory, remainingDirectories, fileName);
902
+ } else {
903
+ newNode = new Models.Directory({
904
+ name: nextDirectoryName,
905
+ open: true
906
+ });
907
+ newDirectory = currentDirectory.collection.add(newNode);
908
+ return this.addToTree(newNode, remainingDirectories, fileName);
909
+ }
910
+ } else {
911
+ if (fileName === "") {
912
+ return null;
913
+ }
914
+ file = Models.File.createFromFileName(fileName, this._currentFileData);
915
+ this._currentFileData = null;
916
+ currentDirectory.collection.add(file);
917
+ if (file.get('autoOpen')) {
918
+ this.trigger('openFile', file);
919
+ }
920
+ return file;
921
+ }
922
+ };
923
+
924
+ Tree.prototype.findOrCreateView = function(node) {
925
+ /*
926
+ Internal: Look up existing view in the view cache or Create a new view
927
+ of the correct type (either File or Directory).
928
+
929
+ * node - A Node object. Either a File object or a Directory object.
930
+ This is the model that the view will be associated with.
931
+
932
+ Examples
933
+
934
+ file = new BoneTree.Models.File
935
+
936
+ # This will create a new view since we just created the File
937
+ tree.findOrCreateView(file)
938
+ # => <FileView>
939
+
940
+ Returns the view corresponding to the model passed in.
941
+ */
942
+
943
+ var type, view, viewCache;
944
+ type = node.constantize();
945
+ viewCache = this.settings.get('viewCache');
946
+ if (!(view = viewCache[node.cid])) {
947
+ view = viewCache[node.cid] = new Views[type]({
948
+ model: node,
949
+ settings: this.settings
950
+ });
951
+ }
952
+ return view;
953
+ };
954
+
955
+ Tree.prototype.getModelByCid = function(cid) {
956
+ var modelCid, view, viewCache;
957
+ viewCache = this.settings.get('viewCache');
958
+ for (modelCid in viewCache) {
959
+ view = viewCache[modelCid];
960
+ if (modelCid === cid) {
961
+ return view.model;
962
+ }
963
+ }
964
+ };
965
+
966
+ Tree.prototype.closeDirectories = function() {
967
+ /*
968
+ Public: Close all the directories in the file tree.
969
+
970
+ Examples
971
+
972
+ tree.closeDirectories()
973
+ # => <Tree>
974
+
975
+ Returns the Tree view object.
976
+ */
977
+
978
+ var directories;
979
+ directories = _.filter(this.flatten(), function(node) {
980
+ return node.get('nodeType') === 'directory';
981
+ });
982
+ _.invoke(directories, 'set', {
983
+ open: false
984
+ });
985
+ return this;
986
+ };
987
+
988
+ Tree.prototype._closeMenu = function(e) {
989
+ /*
990
+ Internal: Close the context menu. This is called every click on
991
+ the document and closes the menu unless you are clicking
992
+ within it. This shouldn't be called directly, it is called
993
+ automatically by Backbone from user interactions.
994
+
995
+ Returns the Menu view object.
996
+ */
997
+ if (!$(e.currentTarget).is('.menu')) {
998
+ this.menuView.$el.hide();
999
+ }
1000
+ return this.menuView;
1001
+ };
1002
+
1003
+ Tree.prototype._contextMenu = function(e) {
1004
+ /*
1005
+ Internal: Open the context menu. This prevents the default browser
1006
+ context menu event. This shouldn't be called directly, it is
1007
+ called automatically by Backbone from user interations.
1008
+
1009
+ Returns the Menu view object.
1010
+ */
1011
+
1012
+ var model;
1013
+ e.preventDefault();
1014
+ model = this.getModelFromClick(e);
1015
+ this.menuView.model = model;
1016
+ this.menuView.$el.css({
1017
+ left: e.pageX,
1018
+ top: e.pageY
1019
+ }).show();
1020
+ return this.menuView;
1021
+ };
1022
+
1023
+ Tree.prototype.filterNodes = function(nodeType, nodeName) {
1024
+ /*
1025
+ Internal: Returns file tree nodes that match the nodeType and nodeName.
1026
+
1027
+ * nodeType - A String that represents the nodeType to match. Choices are
1028
+ 'file' or 'directory'.
1029
+ * nodeName - A String that represents the name of the node to match.
1030
+
1031
+ Examples
1032
+
1033
+ # Add some files to the tree
1034
+ tree.file('/source/main.coffee')
1035
+ tree.file('/source/player.coffee')
1036
+
1037
+ # returns an array containing the File 'main.coffee'
1038
+ tree.filterNodes('file', 'main')
1039
+ # => [<File>]
1040
+
1041
+ Returns an Array of nodes that match the filter criteria.
1042
+ */
1043
+
1044
+ var results,
1045
+ _this = this;
1046
+ results = _.filter(this.flatten(), function(node) {
1047
+ return node.get('nodeType') === nodeType && node.get('name') === nodeName;
1048
+ });
1049
+ return results;
1050
+ };
1051
+
1052
+ Tree.prototype.flatten = function(currentNode, results) {
1053
+ var _this = this;
1054
+ if (currentNode == null) {
1055
+ currentNode = this.root;
1056
+ }
1057
+ if (results == null) {
1058
+ results = [];
1059
+ }
1060
+ /*
1061
+ Internal: Returns a one dimensional ordered array representing the
1062
+ Directory and File nodes in the tree.
1063
+
1064
+ * currentNode - The node to start at when flattening
1065
+ * nodeName - A String that represents the name of the node to match.
1066
+
1067
+ Examples
1068
+
1069
+ # Add some files to the tree
1070
+ tree.file('/source/main.coffee', {aFile: true})
1071
+ tree.file('/source/player.coffee', {playerData: {x: 50, y: 30}})
1072
+
1073
+ # returns an array containing the File 'main.coffee'
1074
+ tree.filterNodes('file', 'main')
1075
+ # => [<File>]
1076
+
1077
+ Returns an Array of nodes that match the filter criteria.
1078
+ */
1079
+
1080
+ currentNode.collection.each(function(node) {
1081
+ results.push(node);
1082
+ if (node.collection.length) {
1083
+ return _this.flatten(node, results);
1084
+ }
1085
+ });
1086
+ return results;
1087
+ };
1088
+
1089
+ Tree.prototype.getDirectory = function(directoryName) {
1090
+ /*
1091
+ Public: Returns an array of directories matching the given directoryName.
1092
+
1093
+ * directoryName - A String naming the directory to match.
1094
+
1095
+ Examples
1096
+
1097
+ # Add some files to the tree
1098
+ tree.file('/source/main.coffee', {size: 4039})
1099
+ tree.file('/source/player.coffee', {size: 399})
1100
+ tree.file('/directory2/file.coffee', {size: 23})
1101
+
1102
+ # returns an array containing the Directory 'source'
1103
+ tree.getDirectory('source')
1104
+ # => [<Directory>]
1105
+
1106
+ Returns an Array of Directory nodes that match directoryName.
1107
+ */
1108
+ return this.filterNodes('directory', directoryName);
1109
+ };
1110
+
1111
+ Tree.prototype._getFile = function(filePath) {
1112
+ /*
1113
+ Internal: Returns a file at the specified location.
1114
+
1115
+ * fileName - A String describing the file path.
1116
+
1117
+ Examples
1118
+
1119
+ # Add some files to the tree
1120
+ tree.file('/source/main.coffee', {size: 30459})
1121
+ tree.file('/source/player.coffee', {size: 943})
1122
+ tree.file('/directory2/main.coffee', {size: 4945})
1123
+
1124
+ # returns an array containing both the files named main.
1125
+ tree._getFile('source/main.coffee')
1126
+ # => <File>
1127
+
1128
+ Returns a File at the given location.
1129
+ */
1130
+
1131
+ var filtered, nodes;
1132
+ if (filePath[0] === '/') {
1133
+ filePath = filePath.replace('/', '');
1134
+ }
1135
+ nodes = this.flatten();
1136
+ filtered = _.filter(nodes, function(node) {
1137
+ return node.get('nodeType') === 'file' && node.get('path') === filePath;
1138
+ });
1139
+ return filtered[0];
1140
+ };
1141
+
1142
+ Tree.prototype.files = function(directoryName) {
1143
+ /*
1144
+ Public: Returns an array of files contained within the directory
1145
+ matching directoryName.
1146
+
1147
+ * directoryName - A String naming the directory to look inside.
1148
+
1149
+ Examples
1150
+
1151
+ # Add some files to the tree
1152
+ tree.file('/source/main.coffee', {main: true})
1153
+ tree.file('/source/player.coffee', {active: true})
1154
+ tree.file('/directory2/main.coffee', {active: true})
1155
+
1156
+ # returns an array containing the files 'player.coffee' and 'main.coffee'
1157
+ tree.files('source')
1158
+ # => [<File>, <File>]
1159
+
1160
+ Returns an Array of File nodes that are contained in the
1161
+ Directory matching directoryName.
1162
+ */
1163
+
1164
+ var directory, nodesInDirectory;
1165
+ if (directoryName == null) {
1166
+ return _.filter(this.flatten(), function(node) {
1167
+ return node.get('nodeType') === 'file';
1168
+ });
1169
+ }
1170
+ directory = this.getDirectory(directoryName)[0];
1171
+ if (!directory) {
1172
+ return [];
1173
+ }
1174
+ nodesInDirectory = this.flatten(directory);
1175
+ return _.filter(nodesInDirectory, function(node) {
1176
+ return node.get('nodeType') === 'file';
1177
+ });
1178
+ };
1179
+
1180
+ Tree.prototype.toAscii = function(collection, indentation, output) {
1181
+ var n, rootCollection, spaces, _i,
1182
+ _this = this;
1183
+ if (indentation == null) {
1184
+ indentation = 0;
1185
+ }
1186
+ if (output == null) {
1187
+ output = "\n";
1188
+ }
1189
+ /*
1190
+ Internal: A String representation of the filetree.
1191
+
1192
+ * collection - A NodeCollection object describing which folder to start at.
1193
+ * indentation - A Number describing how many spaces to indent the next filetree element (default: 0).
1194
+ * output - A String representing the current filetree output (default: "\n").
1195
+
1196
+ Examples
1197
+
1198
+ # Add some files to the tree
1199
+ tree.file('/source/main.coffee', {main: true})
1200
+ tree.file('/source/player.coffee', {active: true})
1201
+ tree.file('/directory2/main.coffee', {active: false})
1202
+
1203
+ tree.toAscii()
1204
+ # => "
1205
+ -directory2
1206
+ -main.coffee
1207
+ -source
1208
+ -main.coffee
1209
+ -player.coffee
1210
+ "
1211
+
1212
+ Returns a String representation of the sorted nodes of the file tree.
1213
+ */
1214
+
1215
+ rootCollection = collection || this.root.collection;
1216
+ spaces = "";
1217
+ for (n = _i = 0; 0 <= indentation ? _i <= indentation : _i >= indentation; n = 0 <= indentation ? ++_i : --_i) {
1218
+ spaces += " ";
1219
+ }
1220
+ rootCollection.each(function(nodes) {
1221
+ var typeChar;
1222
+ typeChar = nodes.get('type') === 'directory' ? '+' : '-';
1223
+ output += spaces + typeChar + nodes.nameWithExtension() + '\n';
1224
+ return output = _this.toAscii(nodes.collection, indentation + 1, output);
1225
+ });
1226
+ return output;
1227
+ };
1228
+
1229
+ Tree.prototype.getModelFromClick = function(e) {
1230
+ /*
1231
+ Internal: Look up a model based on the cid of the clicked view element.
1232
+
1233
+ Returns the Node corresponding to the view element that the user clicked on.
1234
+ */
1235
+
1236
+ var cid;
1237
+ e.stopPropagation();
1238
+ this.menuView.$el.hide();
1239
+ cid = $(e.currentTarget).data('cid');
1240
+ return this.getModelByCid(cid);
1241
+ };
1242
+
1243
+ Tree.prototype._openDirectory = function(e) {
1244
+ /*
1245
+ Internal: Toggle the directory icon and display the contents of the clicked Directory.
1246
+ */
1247
+
1248
+ var model;
1249
+ model = this.getModelFromClick(e);
1250
+ return model.toggleOpen();
1251
+ };
1252
+
1253
+ Tree.prototype._openFile = function(e) {
1254
+ /*
1255
+ Internal: Trigger the 'openFile' event, passing in the file corresponding
1256
+ to the view element that the user clicked.
1257
+ */
1258
+
1259
+ var model;
1260
+ model = this.getModelFromClick(e);
1261
+ return this.trigger('openFile', model);
1262
+ };
1263
+
1264
+ Tree.prototype.render = function() {
1265
+ /*
1266
+ Internal: Call render on each of the nodes underneath the root node.
1267
+ Also calls sort on each of the subcollections.
1268
+ */
1269
+
1270
+ var _this = this;
1271
+ this.root.collection.sort().each(function(node) {
1272
+ var view;
1273
+ node.collection.sort();
1274
+ view = _this.findOrCreateView(node);
1275
+ if (!view.model.get('hidden')) {
1276
+ return _this.$el.append(view.render().$el);
1277
+ }
1278
+ });
1279
+ return this;
1280
+ };
1281
+
1282
+ return Tree;
1283
+
1284
+ })(Backbone.View);
1285
+ });
1286
+
1287
+ }).call(this);
1288
+ (function() {
1289
+
1290
+
1291
+
1292
+ }).call(this);
data/lib/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module BoneTree
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.4"
3
3
  end
4
4
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bone_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -41,7 +41,7 @@ files:
41
41
  - docs/source/javascripts/bone_tree/views/_file.js.html
42
42
  - docs/source/javascripts/bone_tree/views/_menu.js.html
43
43
  - docs/source/javascripts/bone_tree/views/_tree.js.html
44
- - lib/assets/bone_tree.js.coffee
44
+ - lib/assets/javascripts/bone_tree.js
45
45
  - lib/version.rb
46
46
  - source/images/bonetree.png
47
47
  - source/images/crushed_bone.png
@@ -85,7 +85,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
85
  version: '0'
86
86
  segments:
87
87
  - 0
88
- hash: -95181981014547945
88
+ hash: -3128340713276580685
89
89
  required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  none: false
91
91
  requirements:
@@ -1 +0,0 @@
1
- #= require ./src/bone_tree