bone_tree 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|