bone_tree 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.
- data/.gitignore +5 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +190 -0
- data/Guardfile +14 -0
- data/README +1 -0
- data/Rakefile +21 -0
- data/app/assets/images/bonetree.png +0 -0
- data/app/assets/images/crushed_bone.png +0 -0
- data/app/assets/index.html +439 -0
- data/app/assets/javascripts/bone_tree.js +1292 -0
- data/app/assets/stylesheets/bone_tree.css +186 -0
- data/app/assets/stylesheets/bone_tree_repo.css +73 -0
- data/bone_tree-0.0.1.gem +0 -0
- data/bone_tree.gemspec +20 -0
- data/config.rb +26 -0
- data/config.ru +4 -0
- data/docs/index.html +222 -0
- data/docs/resources/base.css +70 -0
- data/docs/resources/index.css +20 -0
- data/docs/resources/module.css +24 -0
- data/docs/source/javascripts/bone_tree/_namespace.js.html +45 -0
- data/docs/source/javascripts/bone_tree/models/_directory.js.html +126 -0
- data/docs/source/javascripts/bone_tree/models/_file.js.html +112 -0
- data/docs/source/javascripts/bone_tree/models/_nodes.js.html +174 -0
- data/docs/source/javascripts/bone_tree/models/_settings.js.html +75 -0
- data/docs/source/javascripts/bone_tree/views/_directory.js.html +94 -0
- data/docs/source/javascripts/bone_tree/views/_file.js.html +82 -0
- data/docs/source/javascripts/bone_tree/views/_menu.js.html +110 -0
- data/docs/source/javascripts/bone_tree/views/_tree.js.html +432 -0
- data/lib/version.rb +4 -0
- data/source/images/bonetree.png +0 -0
- data/source/images/crushed_bone.png +0 -0
- data/source/index.html.haml +438 -0
- data/source/javascripts/_backbone.js +1293 -0
- data/source/javascripts/_jquery.min.js +5 -0
- data/source/javascripts/_underscore.js +999 -0
- data/source/javascripts/bone_tree/_namespace.js.coffee +7 -0
- data/source/javascripts/bone_tree/models/_directory.js.coffee +63 -0
- data/source/javascripts/bone_tree/models/_file.js.coffee +55 -0
- data/source/javascripts/bone_tree/models/_nodes.js.coffee +117 -0
- data/source/javascripts/bone_tree/models/_settings.js.coffee +25 -0
- data/source/javascripts/bone_tree/views/_directory.js.coffee +73 -0
- data/source/javascripts/bone_tree/views/_file.js.coffee +49 -0
- data/source/javascripts/bone_tree/views/_menu.js.coffee +97 -0
- data/source/javascripts/bone_tree/views/_tree.js.coffee +498 -0
- data/source/javascripts/bone_tree.js.coffee +1 -0
- data/source/layout.haml +13 -0
- data/source/stylesheets/bone_tree.css.sass +107 -0
- data/source/stylesheets/bone_tree_repo.css.sass +65 -0
- data/spec/javascripts/directory_view_spec.coffee +91 -0
- data/spec/javascripts/file_view_spec.coffee +70 -0
- data/spec/javascripts/helpers/spec_helper.coffee +7 -0
- data/spec/javascripts/menu_view_spec.coffee +42 -0
- data/spec/javascripts/nodes_spec.coffee +37 -0
- data/spec/javascripts/support/jasmine.yml +8 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_runner.rb +32 -0
- data/spec/javascripts/tree_view_spec.coffee +39 -0
- metadata +123 -0
@@ -0,0 +1,498 @@
|
|
1
|
+
#= require ../_namespace
|
2
|
+
|
3
|
+
#= require_tree ../models
|
4
|
+
#= require_tree ../views
|
5
|
+
|
6
|
+
BoneTree.namespace "BoneTree.Views", (Views) ->
|
7
|
+
{Models} = BoneTree
|
8
|
+
|
9
|
+
class Views.Tree extends Backbone.View
|
10
|
+
###
|
11
|
+
Public: The base tree object. Events from other objects are proxied to the tree
|
12
|
+
so API consumers only need to know about this top level object.
|
13
|
+
|
14
|
+
###
|
15
|
+
className: 'tree'
|
16
|
+
|
17
|
+
events:
|
18
|
+
'contextmenu .file': '_contextMenu'
|
19
|
+
'contextmenu .directory': '_contextMenu'
|
20
|
+
'click .directory': '_openDirectory'
|
21
|
+
'click .file': '_openFile'
|
22
|
+
|
23
|
+
initialize: ->
|
24
|
+
###
|
25
|
+
Public: Initialize a new filetree widget
|
26
|
+
|
27
|
+
* options - An Object of global configuration options for the file tree.
|
28
|
+
* confirmDeletes - A Boolean. If true, the tree will prompt the user, making
|
29
|
+
sure they want to delete the file (default: false).
|
30
|
+
* showExtensions - A Boolean. If true, files display their extensions. Internally,
|
31
|
+
extensions are always kept track of but by default they are
|
32
|
+
hidden (default: false).
|
33
|
+
|
34
|
+
###
|
35
|
+
$(document).click @_closeMenu
|
36
|
+
|
37
|
+
@_currentFileData = null
|
38
|
+
|
39
|
+
settingsConfig = _.extend({}, @options, {treeView: @})
|
40
|
+
|
41
|
+
@settings = new Models.Settings(settingsConfig)
|
42
|
+
|
43
|
+
@menuView = new Views.Menu
|
44
|
+
settings: @settings
|
45
|
+
@menuView.render().$el.appendTo $('body')
|
46
|
+
|
47
|
+
@root = new Models.Node
|
48
|
+
|
49
|
+
@root.collection.bind 'add', @render
|
50
|
+
|
51
|
+
@root.collection.bind 'remove', (model, collection) =>
|
52
|
+
@$("[data-cid='#{model.cid}']").remove()
|
53
|
+
|
54
|
+
@render()
|
55
|
+
|
56
|
+
@trigger 'remove', model
|
57
|
+
|
58
|
+
file: (filePath, fileData) =>
|
59
|
+
filePath = filePath.replace('/', '') if filePath[0] is '/'
|
60
|
+
|
61
|
+
if fileData?
|
62
|
+
@_currentFileData = _.extend(fileData, path: filePath)
|
63
|
+
|
64
|
+
@_currentFileData.autoOpen = true unless @_currentFileData.autoOpen?
|
65
|
+
@_currentFileData.hidden = false unless @_currentFileData.hidden?
|
66
|
+
else
|
67
|
+
return @_getFile(filePath)
|
68
|
+
|
69
|
+
[dirs..., fileName] = filePath.split '/'
|
70
|
+
|
71
|
+
if file = @_getFile(filePath)
|
72
|
+
file.set(@_currentFileData)
|
73
|
+
else
|
74
|
+
@addToTree(@root, dirs, fileName)
|
75
|
+
|
76
|
+
addFromJSON: (data, currentPath="") =>
|
77
|
+
###
|
78
|
+
Public: Creates a file tree from a JSON representation. Expects the
|
79
|
+
JSON object to have a `name` property at each level, specifying
|
80
|
+
the name of the file or directory, and a files array if the
|
81
|
+
current node has child directories or files.
|
82
|
+
|
83
|
+
* data - An Object that represents hierarchical file data.
|
84
|
+
|
85
|
+
* currentPath - A String representing the current location in the tree.
|
86
|
+
Defaults to the file tree root. (default: "")
|
87
|
+
|
88
|
+
Examples
|
89
|
+
|
90
|
+
data = {
|
91
|
+
name: "My Project"
|
92
|
+
files: [
|
93
|
+
{ name: "Empty Folder" }
|
94
|
+
{ name: "SomeFile.coffee" }
|
95
|
+
{ name: "AnotherFile.coffee" }
|
96
|
+
{
|
97
|
+
name: "Folder with Files inside"
|
98
|
+
files: [
|
99
|
+
{ name: "NestedFile.coffee" }
|
100
|
+
]
|
101
|
+
}
|
102
|
+
]
|
103
|
+
}
|
104
|
+
|
105
|
+
tree.addFromJSON(data)
|
106
|
+
# => <Tree>
|
107
|
+
|
108
|
+
Returns the Tree view object.
|
109
|
+
###
|
110
|
+
name = ""
|
111
|
+
|
112
|
+
if data.name?
|
113
|
+
name = data.name + '/'
|
114
|
+
delete data.name
|
115
|
+
|
116
|
+
if data.extension?
|
117
|
+
name = name.replace('/', '.' + data.extension)
|
118
|
+
delete data.extension
|
119
|
+
|
120
|
+
currentPath += name
|
121
|
+
|
122
|
+
if data.files?
|
123
|
+
for file in data.files
|
124
|
+
@addFromJSON(file, currentPath)
|
125
|
+
else
|
126
|
+
@file(currentPath, data)
|
127
|
+
|
128
|
+
return @
|
129
|
+
|
130
|
+
addToTree: (currentDirectory, remainingDirectories, fileName) =>
|
131
|
+
###
|
132
|
+
Internal: Recursive method that traverses nodes, creating
|
133
|
+
Files and Directories.
|
134
|
+
|
135
|
+
* currentDirectory - A Node object representing which directory we are
|
136
|
+
adding the current Directory or File to.
|
137
|
+
* remainingDirectories - A '/' separated String representing the remaining
|
138
|
+
directories to add.
|
139
|
+
* fileName - The String name of the file to be added. This can
|
140
|
+
include the extension name separated by a '.'.
|
141
|
+
|
142
|
+
Examples
|
143
|
+
|
144
|
+
tree.addToTree(@root, '/source/subdirectory/', 'main.coffee')
|
145
|
+
# => <File>
|
146
|
+
|
147
|
+
Returns the File object if it was created and null if no file was given.
|
148
|
+
###
|
149
|
+
if remainingDirectories.length
|
150
|
+
nextDirectoryName = remainingDirectories.shift()
|
151
|
+
|
152
|
+
if matchingDirectory = Models.Directory.find(currentDirectory, nextDirectoryName)
|
153
|
+
matchingDirectory.set
|
154
|
+
open: true
|
155
|
+
|
156
|
+
@addToTree(matchingDirectory, remainingDirectories, fileName)
|
157
|
+
else
|
158
|
+
newNode = new Models.Directory {name: nextDirectoryName, open: true}
|
159
|
+
|
160
|
+
newDirectory = currentDirectory.collection.add newNode
|
161
|
+
@addToTree(newNode, remainingDirectories, fileName)
|
162
|
+
else
|
163
|
+
return null if fileName is ""
|
164
|
+
|
165
|
+
file = Models.File.createFromFileName(fileName, @_currentFileData)
|
166
|
+
@_currentFileData = null
|
167
|
+
|
168
|
+
currentDirectory.collection.add file
|
169
|
+
|
170
|
+
if file.get('autoOpen')
|
171
|
+
@trigger 'openFile', file
|
172
|
+
|
173
|
+
return file
|
174
|
+
|
175
|
+
findOrCreateView: (node) =>
|
176
|
+
###
|
177
|
+
Internal: Look up existing view in the view cache or Create a new view
|
178
|
+
of the correct type (either File or Directory).
|
179
|
+
|
180
|
+
* node - A Node object. Either a File object or a Directory object.
|
181
|
+
This is the model that the view will be associated with.
|
182
|
+
|
183
|
+
Examples
|
184
|
+
|
185
|
+
file = new BoneTree.Models.File
|
186
|
+
|
187
|
+
# This will create a new view since we just created the File
|
188
|
+
tree.findOrCreateView(file)
|
189
|
+
# => <FileView>
|
190
|
+
|
191
|
+
Returns the view corresponding to the model passed in.
|
192
|
+
###
|
193
|
+
type = node.constantize()
|
194
|
+
viewCache = @settings.get 'viewCache'
|
195
|
+
|
196
|
+
unless view = viewCache[node.cid]
|
197
|
+
view = viewCache[node.cid] = new Views[type]
|
198
|
+
model: node
|
199
|
+
settings: @settings
|
200
|
+
|
201
|
+
return view
|
202
|
+
|
203
|
+
# TODO this seems unneeded and backward. I shouldn't need to look up
|
204
|
+
# a model from the view cache. I should be able to just find it in
|
205
|
+
# the collection.
|
206
|
+
getModelByCid: (cid) =>
|
207
|
+
viewCache = @settings.get 'viewCache'
|
208
|
+
|
209
|
+
for modelCid, view of viewCache
|
210
|
+
return view.model if modelCid is cid
|
211
|
+
|
212
|
+
closeDirectories: =>
|
213
|
+
###
|
214
|
+
Public: Close all the directories in the file tree.
|
215
|
+
|
216
|
+
Examples
|
217
|
+
|
218
|
+
tree.closeDirectories()
|
219
|
+
# => <Tree>
|
220
|
+
|
221
|
+
Returns the Tree view object.
|
222
|
+
###
|
223
|
+
directories = _.filter(@flatten(), (node) ->
|
224
|
+
node.get('nodeType') is 'directory'
|
225
|
+
)
|
226
|
+
|
227
|
+
_.invoke(directories, 'set', {open: false})
|
228
|
+
|
229
|
+
return @
|
230
|
+
|
231
|
+
_closeMenu: (e) =>
|
232
|
+
###
|
233
|
+
Internal: Close the context menu. This is called every click on
|
234
|
+
the document and closes the menu unless you are clicking
|
235
|
+
within it. This shouldn't be called directly, it is called
|
236
|
+
automatically by Backbone from user interactions.
|
237
|
+
|
238
|
+
Returns the Menu view object.
|
239
|
+
###
|
240
|
+
@menuView.$el.hide() unless $(e.currentTarget).is('.menu')
|
241
|
+
|
242
|
+
return @menuView
|
243
|
+
|
244
|
+
_contextMenu: (e) =>
|
245
|
+
###
|
246
|
+
Internal: Open the context menu. This prevents the default browser
|
247
|
+
context menu event. This shouldn't be called directly, it is
|
248
|
+
called automatically by Backbone from user interations.
|
249
|
+
|
250
|
+
Returns the Menu view object.
|
251
|
+
###
|
252
|
+
e.preventDefault()
|
253
|
+
|
254
|
+
model = @getModelFromClick(e)
|
255
|
+
|
256
|
+
@menuView.model = model
|
257
|
+
|
258
|
+
@menuView.$el.css(
|
259
|
+
left: e.pageX
|
260
|
+
top: e.pageY
|
261
|
+
).show()
|
262
|
+
|
263
|
+
return @menuView
|
264
|
+
|
265
|
+
filterNodes: (nodeType, nodeName) =>
|
266
|
+
###
|
267
|
+
Internal: Returns file tree nodes that match the nodeType and nodeName.
|
268
|
+
|
269
|
+
* nodeType - A String that represents the nodeType to match. Choices are
|
270
|
+
'file' or 'directory'.
|
271
|
+
* nodeName - A String that represents the name of the node to match.
|
272
|
+
|
273
|
+
Examples
|
274
|
+
|
275
|
+
# Add some files to the tree
|
276
|
+
tree.file('/source/main.coffee')
|
277
|
+
tree.file('/source/player.coffee')
|
278
|
+
|
279
|
+
# returns an array containing the File 'main.coffee'
|
280
|
+
tree.filterNodes('file', 'main')
|
281
|
+
# => [<File>]
|
282
|
+
|
283
|
+
Returns an Array of nodes that match the filter criteria.
|
284
|
+
###
|
285
|
+
results = _.filter @flatten(), (node) =>
|
286
|
+
node.get('nodeType') is nodeType and node.get('name') is nodeName
|
287
|
+
|
288
|
+
return results
|
289
|
+
|
290
|
+
flatten: (currentNode=@root, results=[]) =>
|
291
|
+
###
|
292
|
+
Internal: Returns a one dimensional ordered array representing the
|
293
|
+
Directory and File nodes in the tree.
|
294
|
+
|
295
|
+
* currentNode - The node to start at when flattening
|
296
|
+
* nodeName - A String that represents the name of the node to match.
|
297
|
+
|
298
|
+
Examples
|
299
|
+
|
300
|
+
# Add some files to the tree
|
301
|
+
tree.file('/source/main.coffee', {aFile: true})
|
302
|
+
tree.file('/source/player.coffee', {playerData: {x: 50, y: 30}})
|
303
|
+
|
304
|
+
# returns an array containing the File 'main.coffee'
|
305
|
+
tree.filterNodes('file', 'main')
|
306
|
+
# => [<File>]
|
307
|
+
|
308
|
+
Returns an Array of nodes that match the filter criteria.
|
309
|
+
###
|
310
|
+
currentNode.collection.each (node) =>
|
311
|
+
results.push node
|
312
|
+
|
313
|
+
@flatten(node, results) if node.collection.length
|
314
|
+
|
315
|
+
return results
|
316
|
+
|
317
|
+
getDirectory: (directoryName) =>
|
318
|
+
###
|
319
|
+
Public: Returns an array of directories matching the given directoryName.
|
320
|
+
|
321
|
+
* directoryName - A String naming the directory to match.
|
322
|
+
|
323
|
+
Examples
|
324
|
+
|
325
|
+
# Add some files to the tree
|
326
|
+
tree.file('/source/main.coffee', {size: 4039})
|
327
|
+
tree.file('/source/player.coffee', {size: 399})
|
328
|
+
tree.file('/directory2/file.coffee', {size: 23})
|
329
|
+
|
330
|
+
# returns an array containing the Directory 'source'
|
331
|
+
tree.getDirectory('source')
|
332
|
+
# => [<Directory>]
|
333
|
+
|
334
|
+
Returns an Array of Directory nodes that match directoryName.
|
335
|
+
###
|
336
|
+
@filterNodes('directory', directoryName)
|
337
|
+
|
338
|
+
_getFile: (filePath) =>
|
339
|
+
###
|
340
|
+
Internal: Returns a file at the specified location.
|
341
|
+
|
342
|
+
* fileName - A String describing the file path.
|
343
|
+
|
344
|
+
Examples
|
345
|
+
|
346
|
+
# Add some files to the tree
|
347
|
+
tree.file('/source/main.coffee', {size: 30459})
|
348
|
+
tree.file('/source/player.coffee', {size: 943})
|
349
|
+
tree.file('/directory2/main.coffee', {size: 4945})
|
350
|
+
|
351
|
+
# returns an array containing both the files named main.
|
352
|
+
tree._getFile('source/main.coffee')
|
353
|
+
# => <File>
|
354
|
+
|
355
|
+
Returns a File at the given location.
|
356
|
+
###
|
357
|
+
|
358
|
+
filePath = filePath.replace('/', '') if filePath[0] is '/'
|
359
|
+
|
360
|
+
nodes = @flatten()
|
361
|
+
|
362
|
+
filtered = _.filter(nodes, (node) ->
|
363
|
+
return node.get('nodeType') is 'file' and node.get('path') is filePath
|
364
|
+
)
|
365
|
+
|
366
|
+
return filtered[0]
|
367
|
+
|
368
|
+
files: (directoryName) =>
|
369
|
+
###
|
370
|
+
Public: Returns an array of files contained within the directory
|
371
|
+
matching directoryName.
|
372
|
+
|
373
|
+
* directoryName - A String naming the directory to look inside.
|
374
|
+
|
375
|
+
Examples
|
376
|
+
|
377
|
+
# Add some files to the tree
|
378
|
+
tree.file('/source/main.coffee', {main: true})
|
379
|
+
tree.file('/source/player.coffee', {active: true})
|
380
|
+
tree.file('/directory2/main.coffee', {active: true})
|
381
|
+
|
382
|
+
# returns an array containing the files 'player.coffee' and 'main.coffee'
|
383
|
+
tree.files('source')
|
384
|
+
# => [<File>, <File>]
|
385
|
+
|
386
|
+
Returns an Array of File nodes that are contained in the
|
387
|
+
Directory matching directoryName.
|
388
|
+
###
|
389
|
+
|
390
|
+
# return all files if no directory is provided
|
391
|
+
unless directoryName?
|
392
|
+
return _.filter(@flatten(), (node) ->
|
393
|
+
return node.get('nodeType') is 'file'
|
394
|
+
)
|
395
|
+
|
396
|
+
directory = @getDirectory(directoryName)[0]
|
397
|
+
|
398
|
+
# short circuit if directory name isn't in the tree
|
399
|
+
# Otherwise flatten will return all the files in
|
400
|
+
# the filetree
|
401
|
+
return [] unless directory
|
402
|
+
|
403
|
+
nodesInDirectory = @flatten(directory)
|
404
|
+
|
405
|
+
return _.filter nodesInDirectory, (node) ->
|
406
|
+
node.get('nodeType') is 'file'
|
407
|
+
|
408
|
+
toAscii: (collection, indentation=0, output="\n") =>
|
409
|
+
###
|
410
|
+
Internal: A String representation of the filetree.
|
411
|
+
|
412
|
+
* collection - A NodeCollection object describing which folder to start at.
|
413
|
+
* indentation - A Number describing how many spaces to indent the next filetree element (default: 0).
|
414
|
+
* output - A String representing the current filetree output (default: "\n").
|
415
|
+
|
416
|
+
Examples
|
417
|
+
|
418
|
+
# Add some files to the tree
|
419
|
+
tree.file('/source/main.coffee', {main: true})
|
420
|
+
tree.file('/source/player.coffee', {active: true})
|
421
|
+
tree.file('/directory2/main.coffee', {active: false})
|
422
|
+
|
423
|
+
tree.toAscii()
|
424
|
+
# => "
|
425
|
+
-directory2
|
426
|
+
-main.coffee
|
427
|
+
-source
|
428
|
+
-main.coffee
|
429
|
+
-player.coffee
|
430
|
+
"
|
431
|
+
|
432
|
+
Returns a String representation of the sorted nodes of the file tree.
|
433
|
+
###
|
434
|
+
rootCollection = collection || @root.collection
|
435
|
+
|
436
|
+
spaces = ""
|
437
|
+
|
438
|
+
for n in [0..indentation]
|
439
|
+
spaces += " "
|
440
|
+
|
441
|
+
rootCollection.each (nodes) =>
|
442
|
+
typeChar = if nodes.get('type') is 'directory' then '+' else '-'
|
443
|
+
|
444
|
+
output += (spaces + typeChar + nodes.nameWithExtension() + '\n')
|
445
|
+
|
446
|
+
output = @toAscii(nodes.collection, indentation + 1, output)
|
447
|
+
|
448
|
+
return output
|
449
|
+
|
450
|
+
getModelFromClick: (e) =>
|
451
|
+
###
|
452
|
+
Internal: Look up a model based on the cid of the clicked view element.
|
453
|
+
|
454
|
+
Returns the Node corresponding to the view element that the user clicked on.
|
455
|
+
###
|
456
|
+
e.stopPropagation()
|
457
|
+
@menuView.$el.hide()
|
458
|
+
|
459
|
+
cid = $(e.currentTarget).data('cid')
|
460
|
+
|
461
|
+
return @getModelByCid(cid)
|
462
|
+
|
463
|
+
_openDirectory: (e) =>
|
464
|
+
###
|
465
|
+
Internal: Toggle the directory icon and display the contents of the clicked Directory.
|
466
|
+
|
467
|
+
###
|
468
|
+
model = @getModelFromClick(e)
|
469
|
+
|
470
|
+
model.toggleOpen()
|
471
|
+
|
472
|
+
_openFile: (e) =>
|
473
|
+
###
|
474
|
+
Internal: Trigger the 'openFile' event, passing in the file corresponding
|
475
|
+
to the view element that the user clicked.
|
476
|
+
|
477
|
+
###
|
478
|
+
model = @getModelFromClick(e)
|
479
|
+
|
480
|
+
# events are emitted by the filetree itself. This way API
|
481
|
+
# consumers don't have to know anything about the internals.
|
482
|
+
@trigger 'openFile', model
|
483
|
+
|
484
|
+
render: =>
|
485
|
+
###
|
486
|
+
Internal: Call render on each of the nodes underneath the root node.
|
487
|
+
Also calls sort on each of the subcollections.
|
488
|
+
|
489
|
+
|
490
|
+
###
|
491
|
+
@root.collection.sort().each (node) =>
|
492
|
+
node.collection.sort()
|
493
|
+
view = @findOrCreateView(node)
|
494
|
+
|
495
|
+
@$el.append view.render().$el unless view.model.get('hidden')
|
496
|
+
|
497
|
+
return @
|
498
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
#= require ./bone_tree/views/_tree
|
data/source/layout.haml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%meta(charset="utf-8")
|
5
|
+
%meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible")
|
6
|
+
|
7
|
+
<link href='http://fonts.googleapis.com/css?family=EB+Garamond' rel='stylesheet' type='text/css'>
|
8
|
+
= stylesheet_link_tag "bone_tree_repo.css"
|
9
|
+
= stylesheet_link_tag "bone_tree.css"
|
10
|
+
= yield_content :head
|
11
|
+
|
12
|
+
%body
|
13
|
+
= yield
|
@@ -0,0 +1,107 @@
|
|
1
|
+
@import "compass"
|
2
|
+
@import "compass/css3"
|
3
|
+
|
4
|
+
$node_height: 20px
|
5
|
+
|
6
|
+
.filetree_context_menu
|
7
|
+
@include global-reset
|
8
|
+
|
9
|
+
+border-radius(3px)
|
10
|
+
+box-shadow(rgba(0, 0, 0, 0.4) 0 2px 10px)
|
11
|
+
|
12
|
+
background-color: #fff
|
13
|
+
border: 1px solid rgba(0, 0, 0, 0.3)
|
14
|
+
cursor: pointer
|
15
|
+
display: none
|
16
|
+
font-weight: 500
|
17
|
+
position: absolute
|
18
|
+
width: 100px
|
19
|
+
|
20
|
+
hr
|
21
|
+
border: 0
|
22
|
+
border-top: 1px solid rgba(0, 0, 0, 0.15)
|
23
|
+
height: 1px
|
24
|
+
margin: 0
|
25
|
+
|
26
|
+
li
|
27
|
+
margin: 0.25em 0
|
28
|
+
padding: 0 0.5em
|
29
|
+
|
30
|
+
&:hover
|
31
|
+
background-color: #1084CE
|
32
|
+
color: white
|
33
|
+
|
34
|
+
.rename
|
35
|
+
background: #fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAACjSURBVDjL7ZNBCsIwEEVz1mwTo1YjiHdIqyWgFBGPonWTC8T2BjlE4JsUwU0ILe7ExUtgPvNmNkMAkG8gPyAwxiAHYwxKKUgpk/kg8N5n4Zwn6865j4CVLXj1AA//rArsW4hAzCil4wTFsUdx6rBuLLaXJ+aH+zTBqukDFpuzxe5qsagnCIbV32vHybF5Wd/GC3JkBfHJEZu11hBCJHPyvwXyAt6tONifnq6xAAAAAElFTkSuQmCC') no-repeat
|
36
|
+
background-position: 4px 3px
|
37
|
+
padding-left: 24px
|
38
|
+
|
39
|
+
.delete
|
40
|
+
background: #fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==') no-repeat
|
41
|
+
background-position: 4px 3px
|
42
|
+
padding-left: 24px
|
43
|
+
|
44
|
+
.tree
|
45
|
+
@include global-reset
|
46
|
+
|
47
|
+
+box-sizing(border-box)
|
48
|
+
-moz-user-select: none
|
49
|
+
-khtml-user-select: none
|
50
|
+
-webkit-user-select: none
|
51
|
+
-o-user-select: none
|
52
|
+
user-select: none
|
53
|
+
|
54
|
+
color: black
|
55
|
+
overflow-y: scroll
|
56
|
+
|
57
|
+
ul, li
|
58
|
+
list-style-type: none
|
59
|
+
|
60
|
+
.directory, .file
|
61
|
+
cursor: pointer
|
62
|
+
|
63
|
+
.directory
|
64
|
+
.directory
|
65
|
+
padding-left: 20px
|
66
|
+
|
67
|
+
.file
|
68
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAL5JREFUeNpi/P//PwMIrOp1gjBwgLDifYxYJUAGgPDKHsf/uMCVvTX/ofIM6JiJgUigbWiN1ZVEG4DLEBYMVXmFqPxJ/QzaTs1w7tXzTgz4DQBqIAVgGNC7YxGc/fnoPoaG5gUM2bnJDFMnz2UgOxaQAXpswF1w7YMcahgAvdJQm8Dw+gMzmAtzAVwdMWEAcj7JYXB1Xy3Djl+qcP7aS5fB9LGybuIMAEWZNhK/2AO/C0hKSARdAAo0UgFAgAEAldGhef3Bv88AAAAASUVORK5CYII=') no-repeat
|
69
|
+
background-position: 1px 3px
|
70
|
+
padding-left: 20px
|
71
|
+
|
72
|
+
&.coffee
|
73
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAILSURBVDjLrVM7ixNhFB2LFKJV+v0L24nabIogtmItin9ALBS3tXNt3CWgVlpMsAgrWRexkCSTd0KimYS8Q94vsnlrikAec7z34hSibON+cJjXPeee79xvFADK/0C5UIFyubxLUEulklooFNR8Pn+Sy+VOstmsmslk1HQ6raZSqd2/BCqVyh4RW/V6HePxGJPJRDCdTuU6Go0EZ2dnIFEkk8lWIpHYEwEi24lszGYzjHptfPvsgvbuEJ9ePMPH548Epwf70N4f4fuXY6rpYDgcIh6PG7FYzM62dSav12spfHXn2rk4fbmPxWIhIpFIRFfIzk+v1wvDMLAhka9vD+B88gCv79lxdPeG4M39W/jw9KF8q+oJzOdz2VIoFPqhOJ3O7mAwwHK5xGazketqtRKws3+Bto1arYZgMFhTHA6HC78XW6P0wYJmcAy2y+9arRYoPCHTpOD3+w8Vm8122xTgQhobqtUqms0mGo0GeDLckdOnESIcDqPdbnN3aJp2VbFarTfN7kxmUqfTkSLuyM8syB3pLMj7fr8Pn883kTFaLJbr1EHfbrdilwm9Xg/dblfABNMF3/NWisUiKPjHIkDrMou43e4CF+m6LkUMU4idcFc+WJwRkbU/TiKtS4QrgUDgmGZrcEcelXkKORsWJ9sGkV3n/kzRaHSHgtrQjEGCHJSAyBuPx7Nz4X/jL/ZqKRurPTy6AAAAAElFTkSuQmCC') no-repeat
|
74
|
+
background-position: 1px 3px
|
75
|
+
padding-left: 20px
|
76
|
+
|
77
|
+
.directory
|
78
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKJJREFUeNpi/P//PwMlgJGqBhzpMEUxzabiNCNBE0AGgPDaYt3/6AAqxoAPg10AstkmOgth6oenDA9ffAUzH5/dg2EpistABhxuN/lPLICqhbuABW7S4/lEBdqVy68ZbJD4TCDi1ZufRIe6mCQfCp+JgUIA9sKnr9/BAUcMAKtFd8GPT7+JthFdLdwL6xauoiwlLshUJSpNJ0y/zUjVvAAQYACWqqflysozQAAAAABJRU5ErkJggg==') no-repeat
|
79
|
+
background-position: 0 4px
|
80
|
+
padding-left: 20px
|
81
|
+
|
82
|
+
&.open
|
83
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMhJREFUeNpi/P//PwMlgJGqBhzpMEUxzabiNCNBE0AGgPDaYt3/6AAqxoAPg10AstkmOgth6oenDA9ffAUzH5/dg2EpistABhxuN/lPLICqhbuABW7S4/lEBdqVy68ZbJD4YANevfmJoXDdxH6sBohJ8jGsK9H7j2IANs1B+YX4nQIMp3ULV0EM+PT1O1gArBkoGBQfBufjAmB1PZcYmUCcH59+o2omAGCa4QlpRrTCf5DfSNWMkhIXZKoSlaYTpt9mpGpeAAgwAJYXxD0Qxy7tAAAAAElFTkSuQmCC') no-repeat
|
84
|
+
background-position: 0 4px
|
85
|
+
|
86
|
+
.json
|
87
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGSSURBVCjPVVFNSwJhEF78Ad79Cf6PvXQRsotUlzKICosuRYmR2RJR0KE6lBFFZVEbpFBSqKu2rum6llFS9HHI4iUhT153n6ZtIWMOM+/MM88z7wwH7s9Ub16SJcnbmrNcxVm2q7Z8/QPvEOtntpj92NkCqITLepEpjix7xQtiLOoQ2b6+E7YAN/5nfOEJ2WbKqOIOJ4bYVMEQx4LfBBQDsvFMhUcCVU1/CxVXmDBGA5ZETrhDCQVcYAPbyEJBhvrnBVPiSpNr6cYDNCQwo4zzU/ySckkgDYuNuVpI42T9k4gLKGMPs/xPzzovQiY2hQYe0jlJfyNNhTqiWDYBq/wBMcSRpnyPzu1oS7WtxjVBSthU1vgVksiQ3Dn6Gp5ah2YOKQo5GiuHPA6xT1EKpxQNCNYejgIR457KKio0S56YckjSa9jo//3mrj+BV0QQagqGTOo+Y7gZIf1puP3WHoLhEb2PjTlCTCWGXtbp8DCX3hZuOdaIc9A+aQvWk4ihq95p67a7nP+u+Ws+r0dql9z/zv0NCYhdCPKZ7oYAAAAASUVORK5CYII=') no-repeat
|
88
|
+
background-position: 0 4px
|
89
|
+
padding-left: 20px
|
90
|
+
|
91
|
+
.png, .jpeg, .gif
|
92
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHwSURBVDjLpZM9a1RBFIafM/fevfcmC7uQjWEjUZKAYBHEVEb/gIWFjVVSWEj6gI0/wt8gprPQykIsTP5BQLAIhBVBzRf52Gw22bk7c8YiZslugggZppuZ55z3nfdICIHrrBhg+ePaa1WZPyk0s+6KWwM1khiyhDcvns4uxQAaZOHJo4nRLMtEJPpnxY6Cd10+fNl4DpwBTqymaZrJ8uoBHfZoyTqTYzvkSRMXlP2jnG8bFYbCXWJGePlsEq8iPQmFA2MijEBhtpis7ZCWftC0LZx3xGnK1ESd741hqqUaqgMeAChgjGDDLqXkgMPTJtZ3KJzDhTZpmtK2OSO5IRB6xvQDRAhOsb5Lx1lOu5ZCHV4B6RLUExvh4s+ZntHhDJAxSqs9TCDBqsc6j0iJdqtMuTROFBkIcllCCGcSytFNfm1tU8k2GRo2pOI43h9ie6tOvTJFbORyDsJFQHKD8fw+P9dWqJZ/I96TdEa5Nb1AOavjVfti0dfB+t4iXhWvyh27y9zEbRRobG7z6fgVeqSoKvB5oIMQEODx7FLvIJo55KS9R7b5ldrDReajpC+Z5z7GAHJFXn1exedVbG36ijwOmJgl0kS7lXtjD0DkLyqc70uPnSuIIwk9QCmWd+9XGnOFDzP/M5xxBInhLYBcd5z/AAZv2pOvFcS/AAAAAElFTkSuQmCC') no-repeat
|
93
|
+
background-position: 0 4px
|
94
|
+
padding-left: 20px
|
95
|
+
|
96
|
+
.js
|
97
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ+SURBVBgZBcExbFRlAADg7//fu7teC3elQEoMgeDkYDQ6oMQQTYyGxMHZuDA6Ypw0cWI20cHJUdl0cJLIiomR6OACGhUCpqGWtlzbu/b97/3v9/tCKQVc/e7RRXz+7OrSpUXbW7S9tu8ddv0M+3iCjF1s42v8WAP0XffKi2eOXfro9dMAYJ766SL1092jfDa17DfZgycHfvh7/hau1QB9161PhgE8epoNQlAHqprRIDo3iqoYDSpeOjv2zHRl7atfNj6LALltJys1Xc9+CmYtTxtmR8yO2D7kv4MMPr7x0KULK54/NThdA+S2XTs+jOYN86MsxqBGVRErKkEV6BHynp//2fXbw9lGDZBTWp+OK7PDzqIpYiyqSMxBFakUVYVS2dxrfHHrrz1crQG6lM6vTwZmR0UHhSoHsSBTKeoS9YU8yLrUXfj+w9d2IkBOzfkz05F5KkKkCkFERACEQil0TSOnJkMNV67fHNdVHI4GUcpZVFAUZAEExEibs4P5osMeROiadHoUiIEeCgFREAoRBOMB2weNrkmbNz+9UiBCTs1yrVdHqhgIkRL0EOj7QGG5jrZ2D+XUbADEy9dunOpSun7xuXMe7xUPNrOd/WyeyKUIoRgOGS8xWWZ7b6FLaROgzim9iXd+vXvf7mHtoCnaXDRtkLpel3t9KdamUx+8fcbj7YWc0hZAndv25XffeGH8yfuvAoBcaHOROhS+vLlhecD+wUJu222AOrft/cdPZr65ddfqsbHVyZLVlZHpysjx5aHRMBrV0XuX141qtnb25bb9F6Duu+7b23funb195955nMRJnMAJTJeGg8HS0sBkZWx1suz3Px79iZ8A/gd7ijssEaZF9QAAAABJRU5ErkJggg==') no-repeat
|
98
|
+
background-position: 0 4px
|
99
|
+
padding-left: 20px
|
100
|
+
|
101
|
+
.wav, .sfs, .mp3, .ogg
|
102
|
+
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAH0SURBVDjLxdPPS9tgGAfwgH/ATmPD0w5jMFa3IXOMFImsOKnbmCUTacW1WZM2Mf1ho6OBrohkIdJfWm9aLKhM6GF4Lz3No/+AMC/PYQXBXL1+95oxh1jGhsgOX/LywvN5n/fN+3IAuKuEuzagVFoO27b1/Z+BcrnUx4otx7FPLWsJvYpIM2SS9H4PqNWqfK1W8VKplHlW/G1zs4G9vS9YXPx4CaDkXOFES4Om4gceUK2WsbZWR72+gtXVFezsbKHVamF7ewtm/sMFgBJZhd6pvm4kDndaAo2KOmt5Gfv7X9HpdNBut9FsNmFZFgPrMHKZc4DkjHyi6KC3MZNehTOuGAH5Xx5ybK/Y3f0Mx3Fg2zaKxSIMw2DjT0inNQ84nogcUUQJHIfZquNT3hzx46DBALizg2o01qEoCqLRKERRRDAYhKYlWRK/AJdCMwH2BY28+Qk8fg667wdXKJjY2FiHaeaRzWYQCk1AEASGzSCZjP/ewtik5r6eBD0dM+nRSMb1j4LuPDnkFhZymJ/PsmLdazmV0jxEkqKsK+niIQ69mKUBwdd9OAx3SADdHtC53FyK12dVXlVlPpF4zytK7OgMyucNyHLs8m+8+2zJHRwG3fId9LxIbNU+OR6zWU57AR5y84FKN+71//EqM2iapfv/HtPf5gcdtKR8VW88PgAAAABJRU5ErkJggg==') no-repeat
|
103
|
+
background-position: 0 4px
|
104
|
+
padding-left: 20px
|
105
|
+
|
106
|
+
|
107
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
@import compass
|
2
|
+
@import compass/css3
|
3
|
+
|
4
|
+
html
|
5
|
+
height: 100%
|
6
|
+
|
7
|
+
body
|
8
|
+
background-image: url('/images/crushed_bone.png')
|
9
|
+
font-family: 'EB Garamond', serif
|
10
|
+
font-variant: small-caps
|
11
|
+
height: 100%
|
12
|
+
margin: 0
|
13
|
+
|
14
|
+
img.background
|
15
|
+
position: absolute
|
16
|
+
top: 20px
|
17
|
+
right: 20px
|
18
|
+
opacity: 0.2
|
19
|
+
|
20
|
+
.bone_tree
|
21
|
+
background-color: rgba(255, 255, 255, 0.5)
|
22
|
+
|
23
|
+
.tree
|
24
|
+
height: 100%
|
25
|
+
padding: 1em
|
26
|
+
|
27
|
+
h1
|
28
|
+
font-size: 2.5em
|
29
|
+
margin: 0
|
30
|
+
padding-top: 0.25em
|
31
|
+
|
32
|
+
h3
|
33
|
+
margin: 0
|
34
|
+
|
35
|
+
ul.features
|
36
|
+
list-style: circle
|
37
|
+
margin-top: 0.25em
|
38
|
+
|
39
|
+
.CodeRay
|
40
|
+
font-family: monaco, monospace
|
41
|
+
font-variant: normal
|
42
|
+
font-size: 12px
|
43
|
+
|
44
|
+
.left
|
45
|
+
border-right: 1px dashed rgba(0, 0, 0, 0.4)
|
46
|
+
height: 100%
|
47
|
+
position: fixed
|
48
|
+
left: 0
|
49
|
+
width: 22%
|
50
|
+
|
51
|
+
.right
|
52
|
+
padding-left: 23%
|
53
|
+
width: 60%
|
54
|
+
|
55
|
+
.background
|
56
|
+
+opacity(0.2)
|
57
|
+
|
58
|
+
background: url('/images/bonetree.png') no-repeat
|
59
|
+
display: inline-block
|
60
|
+
width: 843px
|
61
|
+
height: 650px
|
62
|
+
position: absolute
|
63
|
+
right: 0
|
64
|
+
top: 20px
|
65
|
+
|