bone_tree 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +17 -0
  3. data/Gemfile.lock +190 -0
  4. data/Guardfile +14 -0
  5. data/README +1 -0
  6. data/Rakefile +21 -0
  7. data/app/assets/images/bonetree.png +0 -0
  8. data/app/assets/images/crushed_bone.png +0 -0
  9. data/app/assets/index.html +439 -0
  10. data/app/assets/javascripts/bone_tree.js +1292 -0
  11. data/app/assets/stylesheets/bone_tree.css +186 -0
  12. data/app/assets/stylesheets/bone_tree_repo.css +73 -0
  13. data/bone_tree-0.0.1.gem +0 -0
  14. data/bone_tree.gemspec +20 -0
  15. data/config.rb +26 -0
  16. data/config.ru +4 -0
  17. data/docs/index.html +222 -0
  18. data/docs/resources/base.css +70 -0
  19. data/docs/resources/index.css +20 -0
  20. data/docs/resources/module.css +24 -0
  21. data/docs/source/javascripts/bone_tree/_namespace.js.html +45 -0
  22. data/docs/source/javascripts/bone_tree/models/_directory.js.html +126 -0
  23. data/docs/source/javascripts/bone_tree/models/_file.js.html +112 -0
  24. data/docs/source/javascripts/bone_tree/models/_nodes.js.html +174 -0
  25. data/docs/source/javascripts/bone_tree/models/_settings.js.html +75 -0
  26. data/docs/source/javascripts/bone_tree/views/_directory.js.html +94 -0
  27. data/docs/source/javascripts/bone_tree/views/_file.js.html +82 -0
  28. data/docs/source/javascripts/bone_tree/views/_menu.js.html +110 -0
  29. data/docs/source/javascripts/bone_tree/views/_tree.js.html +432 -0
  30. data/lib/version.rb +4 -0
  31. data/source/images/bonetree.png +0 -0
  32. data/source/images/crushed_bone.png +0 -0
  33. data/source/index.html.haml +438 -0
  34. data/source/javascripts/_backbone.js +1293 -0
  35. data/source/javascripts/_jquery.min.js +5 -0
  36. data/source/javascripts/_underscore.js +999 -0
  37. data/source/javascripts/bone_tree/_namespace.js.coffee +7 -0
  38. data/source/javascripts/bone_tree/models/_directory.js.coffee +63 -0
  39. data/source/javascripts/bone_tree/models/_file.js.coffee +55 -0
  40. data/source/javascripts/bone_tree/models/_nodes.js.coffee +117 -0
  41. data/source/javascripts/bone_tree/models/_settings.js.coffee +25 -0
  42. data/source/javascripts/bone_tree/views/_directory.js.coffee +73 -0
  43. data/source/javascripts/bone_tree/views/_file.js.coffee +49 -0
  44. data/source/javascripts/bone_tree/views/_menu.js.coffee +97 -0
  45. data/source/javascripts/bone_tree/views/_tree.js.coffee +498 -0
  46. data/source/javascripts/bone_tree.js.coffee +1 -0
  47. data/source/layout.haml +13 -0
  48. data/source/stylesheets/bone_tree.css.sass +107 -0
  49. data/source/stylesheets/bone_tree_repo.css.sass +65 -0
  50. data/spec/javascripts/directory_view_spec.coffee +91 -0
  51. data/spec/javascripts/file_view_spec.coffee +70 -0
  52. data/spec/javascripts/helpers/spec_helper.coffee +7 -0
  53. data/spec/javascripts/menu_view_spec.coffee +42 -0
  54. data/spec/javascripts/nodes_spec.coffee +37 -0
  55. data/spec/javascripts/support/jasmine.yml +8 -0
  56. data/spec/javascripts/support/jasmine_config.rb +23 -0
  57. data/spec/javascripts/support/jasmine_runner.rb +32 -0
  58. data/spec/javascripts/tree_view_spec.coffee +39 -0
  59. 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
@@ -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
+