Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
207cec4
make workspace sidebar use a singleton instead of inferring
philschatz Nov 1, 2013
89456e2
add code to hide Book Picker title
philschatz Nov 2, 2013
e59a0f3
if model does not have .findDescendantDFS then use the original model
philschatz Nov 5, 2013
f0a1a25
for pointer events splice in the pointer model
philschatz Nov 5, 2013
1985fbf
remove unused (and poorly defined) trigger
philschatz Nov 5, 2013
b191511
Merge branch 'master' into atc-root-singleton
philschatz Nov 5, 2013
a90f546
Merge branch 'master' into atc-root-singleton
philschatz Nov 5, 2013
ef8e3cd
Merge remote-tracking branch 'origin/master' into atc-root-singleton
izak Nov 7, 2013
6672d7f
add fix so new Modules are added properly to the Workspace
philschatz Nov 8, 2013
16efe78
TocPointerNode.getRoot() returns the OPF file instead of the EPUBCont…
philschatz Nov 20, 2013
f7895ed
look up the root node when calling addChild
philschatz Nov 25, 2013
0dbbffa
merge from master
philschatz Nov 26, 2013
cdab413
update version of less
philschatz Nov 27, 2013
7b36e15
fix adding a new book. no longer throws an error
philschatz Nov 27, 2013
61c900e
disable save when new book is added
philschatz Nov 27, 2013
5dde62b
fix: do not mark book as dirty when it is selected
philschatz Dec 2, 2013
918ea15
add singleton instance getter to epub-container
TomWoodward Dec 2, 2013
b406647
remove crufy code in sidebar.coffee
TomWoodward Dec 2, 2013
0402168
fix removing book
TomWoodward Dec 2, 2013
8d88562
fix: clicking a module in a book in the picker opens the book in the …
philschatz Dec 3, 2013
f860dae
Merge branch 'atc-root-singleton' of github.com:oerpub/github-bookedi…
philschatz Dec 3, 2013
c0da2c8
Merge branch 'master' into atc-root-singleton
philschatz Dec 3, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion scripts/app.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ define [
'cs!session'
'cs!collections/content'
'cs!collections/media-types'
'cs!models/workspace-root'
'cs!models/content/book'
'cs!models/content/book-toc-node'
'cs!models/content/folder'
'cs!models/content/module'
'less!styles/main.less'
], (Backbone, Marionette, session, allContent, mediaTypes, Book, BookTocNode, Folder, Module) ->
], (Backbone, Marionette, session, allContent, mediaTypes, workspaceRoot, Book, BookTocNode, Folder, Module) ->

app = new Marionette.Application()

Expand All @@ -32,6 +33,8 @@ define [
# set the main div for all the layouts
controller.main = @main

controller.setRootNode(workspaceRoot)

# The controller fires a navigate event, the app then updates the url
# as it sees fit.
controller.on 'navigate', (route) -> controller.navigate route
Expand Down
4 changes: 3 additions & 1 deletion scripts/controllers/routing.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ define [
# Only reason to extend Backbone.Router is to get the @navigate method
return new class AppController extends Marionette.AppRouter

setRootNode: (@rootNode) ->

# For all the views ensure there is a layout.
# There is a cyclic dependency between the controller and `menuLayout`
# because `menuLayout` has a "Home" button.
Expand All @@ -31,7 +33,7 @@ define [
# the user can click an item in the ToC to `goEdit`.
_showWorkspacePane: (SidebarView) ->
if not @layout.workspace.currentView
@layout.workspace.show(new SidebarView {collection:allContent})
@layout.workspace.show(new SidebarView {model:@rootNode})


# Show Workspace
Expand Down
14 changes: 5 additions & 9 deletions scripts/gh-book/app.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ define [
onceAll = (promises) -> return $.when.apply($, promises)

# Singleton that gets reloaded when the repo changes
epubContainer = new EpubContainer()
epubContainer = EpubContainer::instance()

allContent.on 'add', (model, collection, options) ->
return if options.loading
Expand All @@ -43,10 +43,6 @@ define [
allContent.each (book) ->
book.manifest?.add(model) # Only books have a manifest

allContent.on 'remove', (model, collection, options) ->
epubContainer.removeChild(model)


# The WelcomeSignInView is overloaded to show Various Dialogs.
#
# - SignIn
Expand All @@ -55,8 +51,6 @@ define [
# When there is a failure show the Settings/SignIn Modal
welcomeView = new WelcomeSignInView {model:session}



# This is a utility that wraps a promise and alerts when the promise fails.
onFail = (promise, message='There was a problem.') ->
complete = 0
Expand Down Expand Up @@ -98,7 +92,7 @@ define [
mediaTypes.add BinaryFile, {mediaType:'image/png'}
mediaTypes.add BinaryFile, {mediaType:'image/jpeg'}

# set which media formats are allowed
# set which media formats are allowed
# at the toplevel of the content
for type in EpubContainer::accept
mediaTypes.type(type)::toplevel = true
Expand Down Expand Up @@ -235,6 +229,8 @@ define [
# Tell the controller which region to put all the views/layouts in
controller.main = App.main

controller.setRootNode(epubContainer)

# Custom routes to configure the Github User and Repo from the browser
router = new class GithubRouter extends Backbone.Router

Expand Down Expand Up @@ -311,7 +307,7 @@ define [
promise = onFail(epubContainer.reload(), 'There was a problem re-loading the repo')
.done () ->
# Get the first book from the epub
opf = epubContainer.children.at(0)
opf = epubContainer.getChildren().at(0)
if opf
opf.load().done () ->
# When that book is loaded, edit it.
Expand Down
27 changes: 18 additions & 9 deletions scripts/gh-book/epub-container.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ define [
'cs!gh-book/opf-file'
], (allContent, Saveable, loadableMixin, treeMixin, OpfFile) ->

instance = undefined
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just return new EpubContainer() at the end of this module? (instead of returning the EpubContainer class)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats what i originally did, but ran into some issue that i forget about and ended up doing it this way. could probably get it rejiggered without too much more effort but i'm not sure if its worth it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just mention it because other singletons in the codebase are written that way (not that I like it; it's difficult to extend a class but it's consistent). routing, app, media-types, allContent are a few examples off the top of my head


class EpubContainer extends Saveable
mediaType: 'application/epub+zip'
accept: [OpfFile::mediaType]
Expand All @@ -23,21 +25,23 @@ define [
urlRoot: ''
id: 'META-INF/container.xml'


initialize: () ->
@children = new Backbone.Collection()

@children.on 'add remove', (collection, options) =>
@_initializeTreeHandlers({root:@})

@getChildren().on 'add remove', (collection, options) =>
@_markDirty(options, true)

@children.on 'reset', (collection, options) =>
@getChildren().on 'reset', (collection, options) =>
return if options.loading

allContent.reset(@children.models)
allContent.reset(@getChildren().models)

# Extend the `load()` to wait until all content is loaded
_loadComplex: (fetchPromise) ->
return fetchPromise.then () =>
contentPromises = @children.map (model) => model.load()
contentPromises = @getChildren().map (model) => model.load()
# Return a new promise that finishes once all the contentPromises have loaded
return $.when.apply($, contentPromises)

Expand All @@ -58,14 +62,14 @@ define [
allContent.add(model, {loading:true})
ret.push model

return @children.reset(ret, {loading:true})
return @getChildren().reset(ret, {loading:true})

serialize: () ->
return if not @$xml

rootfiles = @$xml.find('rootfiles').empty()

@children.each (child) ->
@getChildren().each (child) ->
jQuery('<rootfile/>')
.attr('media-type', child.mediaType)
.attr('full-path', child.id)
Expand All @@ -76,11 +80,16 @@ define [
serializer.serializeToString(@$xml.get(0))

# Called by `loadableMixin.reload` when the repo settings change
reset: () -> @children.reset()
reset: () -> @getChildren().reset()

addChild: (book) -> @children.add(book)
addChild: (book) -> @getChildren().add(book)
removeChild: (book) -> @children.remove(book)

EpubContainer = EpubContainer.extend loadableMixin
EpubContainer = EpubContainer.extend treeMixin

EpubContainer::instance = () ->
return instance || instance = new EpubContainer()

# All content in the Workspace
return EpubContainer
9 changes: 8 additions & 1 deletion scripts/gh-book/opf-file.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ define [
# save opf files on creation
@_save() if @_isNew

# return null so `TocPointerNode.getRoot()` returns the OPF file instead of the EPUBContainer for new books
# HACK because when a new book is added to EPUBComtainer the parent is set.
# Then, in toc-branch, goEdit uses model.getRoot() to determine what to render in the sidebar
getParent: () -> null

# Add an `<item>` to the OPF.
# Called from `@manifest.add` and `@resolveSaveConflict`
_addItem: (model, options={}, force=true) ->
Expand Down Expand Up @@ -416,7 +421,9 @@ define [

# Override the tree's removeMe (which just asks the parent to remove the child)
removeMe: ->
allContent.remove(@).save()
require ['cs!gh-book/epub-container'], (EpubContainer) =>
EpubContainer::instance().removeChild(@)
allContent.save()

newNode: (options) ->
model = options.model
Expand Down
3 changes: 0 additions & 3 deletions scripts/mixins/loadable.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ define ['jquery'], ($) ->
@_loading.fail (err) =>
# Since we failed clear the promise so we can try again later.
@_loading = null
@trigger('load:fail', err)
@_loading.done () =>
@trigger('load:done')

return @_loading

Expand Down
10 changes: 2 additions & 8 deletions scripts/mixins/tree.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ define ['backbone'], (Backbone) ->
throw 'BUG: Missing root' if not options.root
#throw 'BUG: Missing title or model' if not options.title

@_tree_root = options.root

@_tree_children = new Backbone.Collection()
@_tree_children = options.children or new Backbone.Collection()

@_tree_children.on 'add', (child, collection, options) =>
# Parent is useful for DnD but since we don't use a `TocNode`
Expand All @@ -33,12 +31,10 @@ define ['backbone'], (Backbone) ->
child._tree_parent.removeChild(child, options) if child._tree_parent and child._tree_parent != @

child._tree_parent = @
child._tree_root = @_tree_root
@trigger 'tree:add', child, collection, options

@_tree_children.on 'remove', (child, collection, options) =>
delete child._tree_parent
delete child._tree_root
@trigger 'tree:remove', child, collection, options

@_tree_children.on 'change', (child, options) =>
Expand Down Expand Up @@ -120,11 +116,9 @@ define ['backbone'], (Backbone) ->
# Before adding the model make sure it's a Tree Node (does it have `._tree_children`.
# If not, wrap it in a node
if ! (model._tree_children)
model = @_tree_root.newNode {model:model}
model = (@getRoot() or @).newNode {model:model}

children.add(model, {at:at})
# When moving from one book to another make sure the root is set
model._tree_root = @_tree_root


return treeMixin
18 changes: 17 additions & 1 deletion scripts/models/content/book.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ define [
'cs!models/utils'
], (_, Backbone, allContent, Module, loadable, TocNode, TocPointerNode, Utils) ->

# Copy/Pasta from Saveable.coffee
INTERNAL_ATTRIBUTES = [
'_original'
'_selected'
'_isDirty'
'_hasRemoteChanges'
]

return class BookModel extends (TocNode.extend loadable)
mediaType: 'application/vnd.org.cnx.collection'
accept: [Module::mediaType, TocNode::mediaType]
Expand Down Expand Up @@ -69,7 +77,11 @@ define [

@tocNodes.on 'add remove tree:change', (model, collection, options) =>
setNavModel(options)
@tocNodes.on 'change reset', (collection, options) =>
# Explicitly enumerate the change properties to listen to so `_selected` does not trigger the node to be marked dirty
@tocNodes.on 'change reset', (model, options) =>
# Do not mark dirty if only "_*" attributes are changed
return if _.isEmpty _.omit(model.changedAttributes?(), INTERNAL_ATTRIBUTES)

setNavModel(options)

# These store the added items since last successful save.
Expand Down Expand Up @@ -242,6 +254,10 @@ define [
@_localTitlesChanged = {}


getRoot: () -> @
# Prevent the whole workspace from loading up in the book sidebar when a Module is clicked in the picker
getParent: () -> null

newNode: (options) ->
model = options.model
node = @tocNodes.get model.id
Expand Down
6 changes: 4 additions & 2 deletions scripts/models/content/inherits/saveable.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ define ['backbone'], (Backbone) ->
@set(attrs)

onSaved: () ->
# Set _isNew before calling `@set(...)` because the Save button will render when `@set()` is called but not when `@isNew` is changed
# and `@isDirty()` returns `@_isNew or get('_isDirty')`
@_isNew = false # Set in loadable

# If the content was just added, squirrel away the content into _ooriginal for visual Diffing later
@set
_original: @serialize?()
_hasRemoteChanges: false
_isDirty: false

@_isNew = false # Set in loadable
11 changes: 9 additions & 2 deletions scripts/models/content/toc-pointer-node.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
define ['cs!./toc-node'], (TocNode) ->
define ['underscore', 'cs!./toc-node'], (_, TocNode) ->

class TocPointerNode extends TocNode
mediaType: 'application/BUG-mediaType-not-set' # This will get overridden to be whatever this node points to
Expand Down Expand Up @@ -27,7 +27,14 @@ define ['cs!./toc-node'], (TocNode) ->
if options.passThroughChanges
@on 'change:title', (model, value, options) => @model.set('title', value, options)

@model.on 'all', () => @trigger.apply @, arguments
@model.on 'all', () =>
# Since some views use filteredCollection (which uses the `model` argument in the event handler, splice in the pointer)
# This causes problems when filteredCollection tries to keep its collection in sync.
# Replace the 2nd arg (the @model) with the pointer (@)
args = _.toArray(arguments)
throw new Error 'BUG: Expecting 2nd argument to be the model this pointer points to' if @model != args[1]
args.splice(1, 1, @)
@trigger.apply @, args

# Set the title on the model if an overridden one has not been set (github-book "shortcut")
# TODO: see how the github-book works with these 2 lines commented: @set('title', @model.get('title')) if not options.title
Expand Down
29 changes: 29 additions & 0 deletions scripts/models/workspace-root.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
define [
'backbone'
'cs!models/content/inherits/saveable'
'cs!mixins/tree'
'cs!models/content/book'
'cs!models/content/module'
'cs!models/content/folder'
'cs!collections/content'
'filtered-collection'
], (Backbone, Saveable, treeMixin, Book, Module, Folder, allContent) ->

return new class WorkspaceRoot extends Saveable.extend(treeMixin)
accept: [Book::mediaType, Module::mediaType, Folder::mediaType]
initialize: (options) ->
super(options)

content = new Backbone.FilteredCollection(null, {collection:allContent})

# Allow `.add` to be called on filtered collections (for new Books)
content.add = allContent.add.bind(allContent)

# Filter the Workspace sidebar to only contain Book and Folder
content.setFilter (model) => return model.mediaType in [Book::mediaType, Folder::mediaType]

@_initializeTreeHandlers {root:@, children:content}

# Just return the node; for a Book this would return options.model wrapped in a TocPointerNode
newNode: (options) ->
return options.model
15 changes: 5 additions & 10 deletions scripts/views/layouts/workspace/sidebar.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ define [
return class Sidebar extends Marionette.Layout
template: sidebarTemplate

initialize: () ->
initialize: (options) ->
@filteredMediaTypes = new Backbone.FilteredCollection(null, {collection:mediaTypes})
@collection = new Backbone.FilteredCollection(null, {collection:@model.getChildren()})

# Filter the "Add" button by what the model accepts
@filteredMediaTypes.setFilter (type) => return type.id in @model.accept

regions:
addContent: '.add-content'
Expand All @@ -30,15 +34,6 @@ define [
model = @model
collection = @collection or model.getChildren?()

if model
# This is a tree sidebar
@filteredMediaTypes.setFilter (type) -> return type.id in model.accept
else
# This is the Picker/Roots Sidebar
collection = new Backbone.FilteredCollection(null, {collection:collection})
collection.setFilter (content) -> return content.getChildren
@filteredMediaTypes.setFilter (type) -> return type.get('modelType')::toplevel

# TODO: Make the collection a FilteredCollection that only shows @model.accepts
@addContent.show(new AddView {context:model, collection:@filteredMediaTypes})
@toc.show(new TocView {model:model, collection:collection})
Expand Down
2 changes: 2 additions & 0 deletions scripts/views/workspace/sidebar/toc-branch.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ define [
if not model.getRoot?()
# Find the 1st leaf node (editable model)
model = model.findDescendantDFS? (model) -> return model.getChildren().isEmpty()
# if @model does not have `.findDescendantDFS` then use the original model
model = model or @model

controller.goEdit(model, model.getRoot?())

Expand Down
3 changes: 3 additions & 0 deletions styles/workspace/sidebar.less
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ aside > div {

}

// Workspace Picker should not have a title so hide it
aside#workspace #workspace-sidebar-toc > h4 { display: none; }

// Feed each panel's width to the <ol> mixin below
aside#workspace > div { // Picker
.x-indent-compensate-panel(@picker-width - 70);
Expand Down
Loading