browse-everything 0.4.5 → 0.5.0

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.
@@ -62,7 +62,7 @@ module BrowseEverything
62
62
  private
63
63
  def auth_flow
64
64
  @csrf ||= {}
65
- DropboxOAuth2Flow.new(config[:app_key], config[:app_secret], connector_response_url(config[:url_options]).to_s,@csrf,:token)
65
+ DropboxOAuth2Flow.new(config[:app_key], config[:app_secret], connector_response_url(config[:url_options]).to_s,@csrf,'token')
66
66
  end
67
67
 
68
68
  def client
@@ -71,4 +71,4 @@ module BrowseEverything
71
71
  end
72
72
 
73
73
  end
74
- end
74
+ end
@@ -1,8 +1,10 @@
1
1
  module BrowseEverything
2
2
  class Engine < ::Rails::Engine
3
3
  initializer :assets do |config|
4
+ config.assets.paths << config.root.join('vendor', 'assets', 'javascripts')
5
+ config.assets.paths << config.root.join('vendor', 'assets', 'stylesheets')
4
6
  Rails.application.config.assets.precompile += %w{ browse_everything.js }
5
7
  Rails.application.config.assets.precompile += %w{ browse_everything.css }
6
8
  end
7
9
  end
8
- end
10
+ end
@@ -9,9 +9,13 @@ module BrowseEverything
9
9
  @size = size
10
10
  @mtime = mtime
11
11
  @container = container
12
- @type = type || @container ? 'directory' : Rack::Mime.mime_type(File.extname(name))
12
+ @type = type || @container ? 'application/x-directory' : Rack::Mime.mime_type(File.extname(name))
13
13
  end
14
14
 
15
+ def relative_parent_path?
16
+ name =~ /^\.\.?$/ ? true : false
17
+ end
18
+
15
19
  def container?
16
20
  @container
17
21
  end
@@ -1,3 +1,3 @@
1
1
  module BrowseEverything
2
- VERSION = "0.4.5"
2
+ VERSION = "0.5.0"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require File.expand_path("config/environment", ENV['RAILS_ROOT'] || File.expand_path("../internal", __FILE__))
2
2
  require 'rspec'
3
+ require 'rspec/its'
3
4
  require 'webmock/rspec'
4
5
  require 'simplecov'
5
6
  require 'vcr'
@@ -14,8 +15,10 @@ VCR.configure do |c|
14
15
  c.configure_rspec_metadata!
15
16
  end
16
17
 
17
- RSpec.configure do |c|
18
- c.treat_symbols_as_metadata_keys_with_true_values = true
18
+ RSpec.configure do |config|
19
+ config.expect_with :rspec do |c|
20
+ c.syntax = [:should, :expect]
21
+ end
19
22
  end
20
23
 
21
24
  module BrowserConfigHelper
@@ -26,7 +26,7 @@ describe BrowseEverything::Browser do
26
26
  }
27
27
 
28
28
  describe "file config" do
29
- before(:each) { File.stub(:read).and_return(file_config) }
29
+ before(:each) { allow(File).to receive(:read).and_return(file_config) }
30
30
  subject { BrowseEverything::Browser.new(url_options) }
31
31
 
32
32
  it "should have 2 providers" do
@@ -69,7 +69,7 @@ describe BrowseEverything::Browser do
69
69
  }
70
70
 
71
71
  it "should complain but continue" do
72
- Rails.logger.should_receive(:warn).with('Unknown provider: foo')
72
+ allow(Rails.logger).to receive(:warn).with('Unknown provider: foo')
73
73
  expect(subject.providers.keys).to eq([:file_system,:drop_box])
74
74
  end
75
75
  end
@@ -12,7 +12,7 @@ describe BrowseEverything::Driver::DropBox, vcr: { cassette_name: 'dropbox', rec
12
12
  'code' => 'FakeDropboxAuthorizationCodeABCDEFG',
13
13
  'state' => 'GjDcUhPNZrZzdsw%2FghBy2A%3D%3D|drop_box'
14
14
  } }
15
- let(:csrf_data) { {token: 'GjDcUhPNZrZzdsw%2FghBy2A%3D%3D'} }
15
+ let(:csrf_data) { {'token' => 'GjDcUhPNZrZzdsw%2FghBy2A%3D%3D'} }
16
16
 
17
17
  it "#validate_config" do
18
18
  expect { BrowseEverything::Driver::DropBox.new({}) }.to raise_error(BrowseEverything::InitializationError)
@@ -1,44 +1,92 @@
1
1
  require File.expand_path('../../spec_helper',__FILE__)
2
2
 
3
3
  describe BrowseEverything::FileEntry do
4
+
4
5
  let(:mtime) { Time.now }
5
- subject {
6
- BrowseEverything::FileEntry.new(
7
- 'file_id_01234', 'my_provider:/location/pa/th/file.m4v',
8
- 'file.m4v', '1.2 GB', mtime, false
9
- )
10
- }
11
-
12
- it "should be a BrowseEverything::FileEntry" do
13
- expect(subject).to be_a BrowseEverything::FileEntry
14
- end
6
+ describe "regular file" do
7
+ subject {
8
+ BrowseEverything::FileEntry.new(
9
+ 'file_id_01234', 'my_provider:/location/pa/th/file.m4v',
10
+ 'file.m4v', '1.2 GB', mtime, false
11
+ )
12
+ }
15
13
 
16
- it "#id" do
17
- expect(subject.id).to eq("file_id_01234")
18
- end
14
+ it "should be a BrowseEverything::FileEntry" do
15
+ expect(subject).to be_a BrowseEverything::FileEntry
16
+ end
19
17
 
20
- it "#location" do
21
- expect(subject.location).to eq("my_provider:/location/pa/th/file.m4v")
22
- end
18
+ it "#id" do
19
+ expect(subject.id).to eq("file_id_01234")
20
+ end
23
21
 
24
- it "#name" do
25
- expect(subject.name).to eq("file.m4v")
26
- end
22
+ it "#location" do
23
+ expect(subject.location).to eq("my_provider:/location/pa/th/file.m4v")
24
+ end
27
25
 
28
- it "#size" do
29
- expect(subject.size).to eq("1.2 GB")
30
- end
26
+ it "#name" do
27
+ expect(subject.name).to eq("file.m4v")
28
+ end
31
29
 
32
- it "#mtime" do
33
- expect(subject.mtime).to eq(mtime)
34
- end
30
+ it "#size" do
31
+ expect(subject.size).to eq("1.2 GB")
32
+ end
35
33
 
36
- it "#type" do
37
- expect(subject.type).to eq("video/mp4")
34
+ it "#mtime" do
35
+ expect(subject.mtime).to eq(mtime)
36
+ end
37
+
38
+ it "#type" do
39
+ expect(subject.type).to eq("video/mp4")
40
+ end
41
+
42
+ it "#container?" do
43
+ expect(subject.container?).to be false
44
+ end
45
+
46
+ it "#relative_parent_path?" do
47
+ expect(subject.relative_parent_path?).to be false
48
+ end
38
49
  end
50
+
51
+ describe "directory" do
52
+ subject {
53
+ BrowseEverything::FileEntry.new(
54
+ 'directory_id_1234', 'my_provider:/location/pa/th',
55
+ 'th', '', mtime, true
56
+ )
57
+ }
58
+
59
+ it "#type" do
60
+ expect(subject.type).to eq("application/x-directory")
61
+ end
62
+
63
+ it "#container?" do
64
+ expect(subject.container?).to be true
65
+ end
39
66
 
40
- it "#container?" do
41
- expect(subject.container?).to be_false
67
+ it "#relative_parent_path?" do
68
+ expect(subject.relative_parent_path?).to be false
69
+ end
42
70
  end
71
+
72
+ describe "parent path" do
73
+ subject {
74
+ BrowseEverything::FileEntry.new(
75
+ 'directory_id_1234', 'my_provider:/location/pa/th',
76
+ '..', '', mtime, true
77
+ )
78
+ }
43
79
 
44
- end
80
+ it "#type" do
81
+ expect(subject.type).to eq("application/x-directory")
82
+ end
83
+
84
+ it "#container?" do
85
+ expect(subject.container?).to be true
86
+ end
87
+
88
+ it "#relative_parent_path?" do
89
+ expect(subject.relative_parent_path?).to be true
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,629 @@
1
+ /*
2
+ * jQuery treetable Plugin 3.2.0
3
+ * http://ludo.cubicphuse.nl/jquery-treetable
4
+ *
5
+ * Copyright 2013, Ludo van den Boom
6
+ * Dual licensed under the MIT or GPL Version 2 licenses.
7
+ */
8
+ (function($) {
9
+ var Node, Tree, methods;
10
+
11
+ Node = (function() {
12
+ function Node(row, tree, settings) {
13
+ var parentId;
14
+
15
+ this.row = row;
16
+ this.tree = tree;
17
+ this.settings = settings;
18
+
19
+ // TODO Ensure id/parentId is always a string (not int)
20
+ this.id = this.row.data(this.settings.nodeIdAttr);
21
+
22
+ // TODO Move this to a setParentId function?
23
+ parentId = this.row.data(this.settings.parentIdAttr);
24
+ if (parentId != null && parentId !== "") {
25
+ this.parentId = parentId;
26
+ }
27
+
28
+ this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]);
29
+ this.expander = $(this.settings.expanderTemplate);
30
+ this.indenter = $(this.settings.indenterTemplate);
31
+ this.children = [];
32
+ this.initialized = false;
33
+ this.treeCell.prepend(this.indenter);
34
+ }
35
+
36
+ Node.prototype.addChild = function(child) {
37
+ return this.children.push(child);
38
+ };
39
+
40
+ Node.prototype.ancestors = function() {
41
+ var ancestors, node;
42
+ node = this;
43
+ ancestors = [];
44
+ while (node = node.parentNode()) {
45
+ ancestors.push(node);
46
+ }
47
+ return ancestors;
48
+ };
49
+
50
+ Node.prototype.collapse = function() {
51
+ if (this.collapsed()) {
52
+ return this;
53
+ }
54
+
55
+ this.row.removeClass("expanded").addClass("collapsed");
56
+
57
+ this._hideChildren();
58
+ this.expander.attr("title", this.settings.stringExpand);
59
+
60
+ if (this.initialized && this.settings.onNodeCollapse != null) {
61
+ this.settings.onNodeCollapse.apply(this);
62
+ }
63
+
64
+ return this;
65
+ };
66
+
67
+ Node.prototype.collapsed = function() {
68
+ return this.row.hasClass("collapsed");
69
+ };
70
+
71
+ // TODO destroy: remove event handlers, expander, indenter, etc.
72
+
73
+ Node.prototype.expand = function() {
74
+ if (this.expanded()) {
75
+ return this;
76
+ }
77
+
78
+ this.row.removeClass("collapsed").addClass("expanded");
79
+
80
+ if (this.initialized && this.settings.onNodeExpand != null) {
81
+ this.settings.onNodeExpand.apply(this);
82
+ }
83
+
84
+ if ($(this.row).is(":visible")) {
85
+ this._showChildren();
86
+ }
87
+
88
+ this.expander.attr("title", this.settings.stringCollapse);
89
+
90
+ return this;
91
+ };
92
+
93
+ Node.prototype.expanded = function() {
94
+ return this.row.hasClass("expanded");
95
+ };
96
+
97
+ Node.prototype.hide = function() {
98
+ this._hideChildren();
99
+ this.row.hide();
100
+ return this;
101
+ };
102
+
103
+ Node.prototype.isBranchNode = function() {
104
+ if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) {
105
+ return true;
106
+ } else {
107
+ return false;
108
+ }
109
+ };
110
+
111
+ Node.prototype.updateBranchLeafClass = function(){
112
+ this.row.removeClass('branch');
113
+ this.row.removeClass('leaf');
114
+ this.row.addClass(this.isBranchNode() ? 'branch' : 'leaf');
115
+ };
116
+
117
+ Node.prototype.level = function() {
118
+ return this.ancestors().length;
119
+ };
120
+
121
+ Node.prototype.parentNode = function() {
122
+ if (this.parentId != null) {
123
+ return this.tree[this.parentId];
124
+ } else {
125
+ return null;
126
+ }
127
+ };
128
+
129
+ Node.prototype.removeChild = function(child) {
130
+ var i = $.inArray(child, this.children);
131
+ return this.children.splice(i, 1)
132
+ };
133
+
134
+ Node.prototype.render = function() {
135
+ var handler,
136
+ settings = this.settings,
137
+ target;
138
+
139
+ if (settings.expandable === true && this.isBranchNode()) {
140
+ handler = function(e) {
141
+ $(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle();
142
+ return e.preventDefault();
143
+ };
144
+
145
+ this.indenter.html(this.expander);
146
+ target = settings.clickableNodeNames === true ? this.treeCell : this.expander;
147
+
148
+ target.off("click.treetable").on("click.treetable", handler);
149
+ target.off("keydown.treetable").on("keydown.treetable", function(e) {
150
+ if (e.keyCode == 13) {
151
+ handler.apply(this, [e]);
152
+ }
153
+ });
154
+ }
155
+
156
+ this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px";
157
+
158
+ return this;
159
+ };
160
+
161
+ Node.prototype.reveal = function() {
162
+ if (this.parentId != null) {
163
+ this.parentNode().reveal();
164
+ }
165
+ return this.expand();
166
+ };
167
+
168
+ Node.prototype.setParent = function(node) {
169
+ if (this.parentId != null) {
170
+ this.tree[this.parentId].removeChild(this);
171
+ }
172
+ this.parentId = node.id;
173
+ this.row.data(this.settings.parentIdAttr, node.id);
174
+ return node.addChild(this);
175
+ };
176
+
177
+ Node.prototype.show = function() {
178
+ if (!this.initialized) {
179
+ this._initialize();
180
+ }
181
+ this.row.show();
182
+ if (this.expanded()) {
183
+ this._showChildren();
184
+ }
185
+ return this;
186
+ };
187
+
188
+ Node.prototype.toggle = function() {
189
+ if (this.expanded()) {
190
+ this.collapse();
191
+ } else {
192
+ this.expand();
193
+ }
194
+ return this;
195
+ };
196
+
197
+ Node.prototype._hideChildren = function() {
198
+ var child, _i, _len, _ref, _results;
199
+ _ref = this.children;
200
+ _results = [];
201
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
202
+ child = _ref[_i];
203
+ _results.push(child.hide());
204
+ }
205
+ return _results;
206
+ };
207
+
208
+ Node.prototype._initialize = function() {
209
+ var settings = this.settings;
210
+
211
+ this.render();
212
+
213
+ if (settings.expandable === true && settings.initialState === "collapsed") {
214
+ this.collapse();
215
+ } else {
216
+ this.expand();
217
+ }
218
+
219
+ if (settings.onNodeInitialized != null) {
220
+ settings.onNodeInitialized.apply(this);
221
+ }
222
+
223
+ return this.initialized = true;
224
+ };
225
+
226
+ Node.prototype._showChildren = function() {
227
+ var child, _i, _len, _ref, _results;
228
+ _ref = this.children;
229
+ _results = [];
230
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
231
+ child = _ref[_i];
232
+ _results.push(child.show());
233
+ }
234
+ return _results;
235
+ };
236
+
237
+ return Node;
238
+ })();
239
+
240
+ Tree = (function() {
241
+ function Tree(table, settings) {
242
+ this.table = table;
243
+ this.settings = settings;
244
+ this.tree = {};
245
+
246
+ // Cache the nodes and roots in simple arrays for quick access/iteration
247
+ this.nodes = [];
248
+ this.roots = [];
249
+ }
250
+
251
+ Tree.prototype.collapseAll = function() {
252
+ var node, _i, _len, _ref, _results;
253
+ _ref = this.nodes;
254
+ _results = [];
255
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
256
+ node = _ref[_i];
257
+ _results.push(node.collapse());
258
+ }
259
+ return _results;
260
+ };
261
+
262
+ Tree.prototype.expandAll = function() {
263
+ var node, _i, _len, _ref, _results;
264
+ _ref = this.nodes;
265
+ _results = [];
266
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
267
+ node = _ref[_i];
268
+ _results.push(node.expand());
269
+ }
270
+ return _results;
271
+ };
272
+
273
+ Tree.prototype.findLastNode = function (node) {
274
+ if (node.children.length > 0) {
275
+ return this.findLastNode(node.children[node.children.length - 1]);
276
+ } else {
277
+ return node;
278
+ }
279
+ };
280
+
281
+ Tree.prototype.loadRows = function(rows) {
282
+ var node, row, i;
283
+
284
+ if (rows != null) {
285
+ for (i = 0; i < rows.length; i++) {
286
+ row = $(rows[i]);
287
+
288
+ if (row.data(this.settings.nodeIdAttr) != null) {
289
+ node = new Node(row, this.tree, this.settings);
290
+ this.nodes.push(node);
291
+ this.tree[node.id] = node;
292
+
293
+ if (node.parentId != null && this.tree[node.parentId]) {
294
+ this.tree[node.parentId].addChild(node);
295
+ } else {
296
+ this.roots.push(node);
297
+ }
298
+ }
299
+ }
300
+ }
301
+
302
+ for (i = 0; i < this.nodes.length; i++) {
303
+ node = this.nodes[i].updateBranchLeafClass();
304
+ }
305
+
306
+ return this;
307
+ };
308
+
309
+ Tree.prototype.move = function(node, destination) {
310
+ // Conditions:
311
+ // 1: +node+ should not be inserted as a child of +node+ itself.
312
+ // 2: +destination+ should not be the same as +node+'s current parent (this
313
+ // prevents +node+ from being moved to the same location where it already
314
+ // is).
315
+ // 3: +node+ should not be inserted in a location in a branch if this would
316
+ // result in +node+ being an ancestor of itself.
317
+ var nodeParent = node.parentNode();
318
+ if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) {
319
+ node.setParent(destination);
320
+ this._moveRows(node, destination);
321
+
322
+ // Re-render parentNode if this is its first child node, and therefore
323
+ // doesn't have the expander yet.
324
+ if (node.parentNode().children.length === 1) {
325
+ node.parentNode().render();
326
+ }
327
+ }
328
+
329
+ if(nodeParent){
330
+ nodeParent.updateBranchLeafClass();
331
+ }
332
+ if(node.parentNode()){
333
+ node.parentNode().updateBranchLeafClass();
334
+ }
335
+ node.updateBranchLeafClass();
336
+ return this;
337
+ };
338
+
339
+ Tree.prototype.removeNode = function(node) {
340
+ // Recursively remove all descendants of +node+
341
+ this.unloadBranch(node);
342
+
343
+ // Remove node from DOM (<tr>)
344
+ node.row.remove();
345
+
346
+ // Remove node from parent children list
347
+ if (node.parentId != null) {
348
+ node.parentNode().removeChild(node);
349
+ }
350
+
351
+ // Clean up Tree object (so Node objects are GC-ed)
352
+ delete this.tree[node.id];
353
+ this.nodes.splice($.inArray(node, this.nodes), 1);
354
+
355
+ return this;
356
+ }
357
+
358
+ Tree.prototype.render = function() {
359
+ var root, _i, _len, _ref;
360
+ _ref = this.roots;
361
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
362
+ root = _ref[_i];
363
+
364
+ // Naming is confusing (show/render). I do not call render on node from
365
+ // here.
366
+ root.show();
367
+ }
368
+ return this;
369
+ };
370
+
371
+ Tree.prototype.sortBranch = function(node, sortFun) {
372
+ // First sort internal array of children
373
+ node.children.sort(sortFun);
374
+
375
+ // Next render rows in correct order on page
376
+ this._sortChildRows(node);
377
+
378
+ return this;
379
+ };
380
+
381
+ Tree.prototype.unloadBranch = function(node) {
382
+ // Use a copy of the children array to not have other functions interfere
383
+ // with this function if they manipulate the children array
384
+ // (eg removeNode).
385
+ var children = node.children.slice(0),
386
+ i;
387
+
388
+ for (i = 0; i < children.length; i++) {
389
+ this.removeNode(children[i]);
390
+ }
391
+
392
+ // Reset node's collection of children
393
+ node.children = [];
394
+
395
+ node.updateBranchLeafClass();
396
+
397
+ return this;
398
+ };
399
+
400
+ Tree.prototype._moveRows = function(node, destination) {
401
+ var children = node.children, i;
402
+
403
+ node.row.insertAfter(destination.row);
404
+ node.render();
405
+
406
+ // Loop backwards through children to have them end up on UI in correct
407
+ // order (see #112)
408
+ for (i = children.length - 1; i >= 0; i--) {
409
+ this._moveRows(children[i], node);
410
+ }
411
+ };
412
+
413
+ // Special _moveRows case, move children to itself to force sorting
414
+ Tree.prototype._sortChildRows = function(parentNode) {
415
+ return this._moveRows(parentNode, parentNode);
416
+ };
417
+
418
+ return Tree;
419
+ })();
420
+
421
+ // jQuery Plugin
422
+ methods = {
423
+ init: function(options, force) {
424
+ var settings;
425
+
426
+ settings = $.extend({
427
+ branchAttr: "ttBranch",
428
+ clickableNodeNames: false,
429
+ column: 0,
430
+ columnElType: "td", // i.e. 'td', 'th' or 'td,th'
431
+ expandable: false,
432
+ expanderTemplate: "<a href='#'>&nbsp;</a>",
433
+ indent: 19,
434
+ indenterTemplate: "<span class='indenter'></span>",
435
+ initialState: "collapsed",
436
+ nodeIdAttr: "ttId", // maps to data-tt-id
437
+ parentIdAttr: "ttParentId", // maps to data-tt-parent-id
438
+ stringExpand: "Expand",
439
+ stringCollapse: "Collapse",
440
+
441
+ // Events
442
+ onInitialized: null,
443
+ onNodeCollapse: null,
444
+ onNodeExpand: null,
445
+ onNodeInitialized: null
446
+ }, options);
447
+
448
+ return this.each(function() {
449
+ var el = $(this), tree;
450
+
451
+ if (force || el.data("treetable") === undefined) {
452
+ tree = new Tree(this, settings);
453
+ tree.loadRows(this.rows).render();
454
+
455
+ el.addClass("treetable").data("treetable", tree);
456
+
457
+ if (settings.onInitialized != null) {
458
+ settings.onInitialized.apply(tree);
459
+ }
460
+ }
461
+
462
+ return el;
463
+ });
464
+ },
465
+
466
+ destroy: function() {
467
+ return this.each(function() {
468
+ return $(this).removeData("treetable").removeClass("treetable");
469
+ });
470
+ },
471
+
472
+ collapseAll: function() {
473
+ this.data("treetable").collapseAll();
474
+ return this;
475
+ },
476
+
477
+ collapseNode: function(id) {
478
+ var node = this.data("treetable").tree[id];
479
+
480
+ if (node) {
481
+ node.collapse();
482
+ } else {
483
+ throw new Error("Unknown node '" + id + "'");
484
+ }
485
+
486
+ return this;
487
+ },
488
+
489
+ expandAll: function() {
490
+ this.data("treetable").expandAll();
491
+ return this;
492
+ },
493
+
494
+ expandNode: function(id) {
495
+ var node = this.data("treetable").tree[id];
496
+
497
+ if (node) {
498
+ if (!node.initialized) {
499
+ node._initialize();
500
+ }
501
+
502
+ node.expand();
503
+ } else {
504
+ throw new Error("Unknown node '" + id + "'");
505
+ }
506
+
507
+ return this;
508
+ },
509
+
510
+ loadBranch: function(node, rows) {
511
+ var settings = this.data("treetable").settings,
512
+ tree = this.data("treetable").tree;
513
+
514
+ // TODO Switch to $.parseHTML
515
+ rows = $(rows);
516
+
517
+ if (node == null) { // Inserting new root nodes
518
+ this.append(rows);
519
+ } else {
520
+ var lastNode = this.data("treetable").findLastNode(node);
521
+ rows.insertAfter(lastNode.row);
522
+ }
523
+
524
+ this.data("treetable").loadRows(rows);
525
+
526
+ // Make sure nodes are properly initialized
527
+ rows.filter("tr").each(function() {
528
+ tree[$(this).data(settings.nodeIdAttr)].show();
529
+ });
530
+
531
+ if (node != null) {
532
+ // Re-render parent to ensure expander icon is shown (#79)
533
+ node.render().expand();
534
+ }
535
+
536
+ return this;
537
+ },
538
+
539
+ move: function(nodeId, destinationId) {
540
+ var destination, node;
541
+
542
+ node = this.data("treetable").tree[nodeId];
543
+ destination = this.data("treetable").tree[destinationId];
544
+ this.data("treetable").move(node, destination);
545
+
546
+ return this;
547
+ },
548
+
549
+ node: function(id) {
550
+ return this.data("treetable").tree[id];
551
+ },
552
+
553
+ removeNode: function(id) {
554
+ var node = this.data("treetable").tree[id];
555
+
556
+ if (node) {
557
+ this.data("treetable").removeNode(node);
558
+ } else {
559
+ throw new Error("Unknown node '" + id + "'");
560
+ }
561
+
562
+ return this;
563
+ },
564
+
565
+ reveal: function(id) {
566
+ var node = this.data("treetable").tree[id];
567
+
568
+ if (node) {
569
+ node.reveal();
570
+ } else {
571
+ throw new Error("Unknown node '" + id + "'");
572
+ }
573
+
574
+ return this;
575
+ },
576
+
577
+ sortBranch: function(node, columnOrFunction) {
578
+ var settings = this.data("treetable").settings,
579
+ prepValue,
580
+ sortFun;
581
+
582
+ columnOrFunction = columnOrFunction || settings.column;
583
+ sortFun = columnOrFunction;
584
+
585
+ if ($.isNumeric(columnOrFunction)) {
586
+ sortFun = function(a, b) {
587
+ var extractValue, valA, valB;
588
+
589
+ extractValue = function(node) {
590
+ var val = node.row.find("td:eq(" + columnOrFunction + ")").text();
591
+ // Ignore trailing/leading whitespace and use uppercase values for
592
+ // case insensitive ordering
593
+ return $.trim(val).toUpperCase();
594
+ }
595
+
596
+ valA = extractValue(a);
597
+ valB = extractValue(b);
598
+
599
+ if (valA < valB) return -1;
600
+ if (valA > valB) return 1;
601
+ return 0;
602
+ };
603
+ }
604
+
605
+ this.data("treetable").sortBranch(node, sortFun);
606
+ return this;
607
+ },
608
+
609
+ unloadBranch: function(node) {
610
+ this.data("treetable").unloadBranch(node);
611
+ return this;
612
+ }
613
+ };
614
+
615
+ $.fn.treetable = function(method) {
616
+ if (methods[method]) {
617
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
618
+ } else if (typeof method === 'object' || !method) {
619
+ return methods.init.apply(this, arguments);
620
+ } else {
621
+ return $.error("Method " + method + " does not exist on jQuery.treetable");
622
+ }
623
+ };
624
+
625
+ // Expose classes to world
626
+ this.TreeTable || (this.TreeTable = {});
627
+ this.TreeTable.Node = Node;
628
+ this.TreeTable.Tree = Tree;
629
+ })(jQuery);