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.
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
+