diff --git a/static/css/ie6.css b/static/css/ie6.css index 7e83f2f..9b86eaf 100644 --- a/static/css/ie6.css +++ b/static/css/ie6.css @@ -4,13 +4,20 @@ #toolbar { position: absolute; - float: none; - width: auto; + width: expression( eval( document.all.center_area && ( document.all.center_area.offsetLeft - 50 ) || 0 ) ); + text-align: right; +} + +#note_tree_area { + left: 0; + width: expression( eval( document.all.center_area && ( document.all.center_area.offsetLeft - 50 ) || 0 ) ); + position: absolute; top: expression( eval( document.compatMode && document.compatMode == 'CSS1Compat' ) ? documentElement.scrollTop + 80 : document.body.scrollTop + 80 ); - margin-left: expression( - document.body.clientWidth < ( 900 / 12 ) * - parseInt( document.body.currentStyle.fontSize ) ? "-120px": "-80px" - ); +} + +#note_tree_area_holder { + overflow: auto; + height: expression( eval( documentElement.offsetHeight - 100 ) ); } #status_area { @@ -27,23 +34,15 @@ #everything_area { width:expression( document.body.clientWidth < ( 1300 / 12 ) * - parseInt( document.body.currentStyle.fontSize ) ? "100%": "78em" + parseInt( document.body.currentStyle.fontSize ) ? "100%": "80em" ); } -#toolbar_area { - width:expression( - document.body.clientWidth < ( 1300 / 12 ) * - parseInt( document.body.currentStyle.fontSize ) ? "18%": "15em" - ); - text-align: right; -} - #center_area { float: right; width:expression( document.body.clientWidth < ( 1300 / 12 ) * - parseInt( document.body.currentStyle.fontSize ) ? "60%": "45em" + parseInt( document.body.currentStyle.fontSize ) ? "56%": "45em" ); } @@ -51,17 +50,6 @@ width: 100%; } -#link_area { - width:expression( - document.body.clientWidth < ( 1300 / 12 ) * - parseInt( document.body.currentStyle.fontSize ) ? "17%": "15em" - ); -} - -#link_area_wrapper { - margin-left: 1.5em; -} - .pulldown { height: expression( this.scrollHeight > 200 ? "200px" : "auto" ); width: expression( this.scrollHeight > 200 ? "12em" : "auto" ); diff --git a/static/css/ie7.css b/static/css/ie7.css index 47d10f4..81157b5 100644 --- a/static/css/ie7.css +++ b/static/css/ie7.css @@ -1,7 +1,19 @@ -#toolbar { - left: 2em; +#note_tree_area { + float: right; + margin-left: -6em; +} + +#note_tree_area_holder { + overflow: auto; + height: expression( eval( documentElement.offsetHeight - 100 ) ); +} + +#center_area { + float: right; + max-width: 59%; } #this_notebook_area_title { margin-top: 1.5em; } + diff --git a/static/css/style.css b/static/css/style.css index bb4e750..30cc2e1 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -28,26 +28,24 @@ img { #everything_area { margin: 0 auto; text-align: center; - width: 78em; - max-width: 100%; + max-width: 80em; } -#toolbar_area { +#left_area { float: left; - width: 15em; - max-width: 18%; + width: 17em; + max-width: 20%; } #toolbar { - width: 15em; - max-width: 18%; - position: fixed; text-align: right; z-index: 1; } #toolbar .button_wrapper { float: right; + margin-left: 15px; + margin-right: 15px; } #toolbar .image_button { @@ -232,17 +230,43 @@ img { background-image: url(/static/images/arrow_down_hover.png); } +#note_tree_area { + position: fixed; + width: 17em; + max-width: 20%; + text-align: left; + line-height: 140%; + top: 80px; + bottom: 1em; + overflow-y: visible; +} + +#note_tree_area_holder { + height: 100%; + overflow-y: auto; + padding-left: 1em; + margin-right: 1em; +} + +#note_tree_area_title { + margin-top: 0.5em; + margin-bottom: 0.25em; +} + #link_area { float: right; text-align: left; + line-height: 140%; + width: 20%; +} + +#link_area_holder { + padding-left: 1em; padding-right: 1em; - line-height: 100%; - width: 15em; - max-width: 17%; } #this_notebook_area_title { - margin-top: 0.5em; + margin-top: 0; margin-bottom: 0.25em; } @@ -500,8 +524,19 @@ img { } .link_area_item { - margin-top: 0.25em; - padding: 0.25em 0.25em 0.25em 0.5em; + padding: 0.2em 0.25em 0.2em 0.5em; +} + +.tree_expander { + float: left; + width: 20px; + height: 1.5em; + background: url(/static/images/tree_arrow.png) no-repeat center center; +} + +.tree_expander:hover { + background: url(/static/images/tree_arrow_hover.png) no-repeat center center; + cursor: pointer; } #storage_usage_area { @@ -607,6 +642,7 @@ img { .small_text { font-size: 90%; + font-weight: normal; } .new_feature_text { diff --git a/static/images/tree_arrow.png b/static/images/tree_arrow.png new file mode 100644 index 0000000..17cbf7d Binary files /dev/null and b/static/images/tree_arrow.png differ diff --git a/static/images/tree_arrow.xcf b/static/images/tree_arrow.xcf new file mode 100644 index 0000000..3fcfd6c Binary files /dev/null and b/static/images/tree_arrow.xcf differ diff --git a/static/images/tree_arrow_down.png b/static/images/tree_arrow_down.png new file mode 100644 index 0000000..93559a1 Binary files /dev/null and b/static/images/tree_arrow_down.png differ diff --git a/static/images/tree_arrow_down.xcf b/static/images/tree_arrow_down.xcf new file mode 100644 index 0000000..d7f0955 Binary files /dev/null and b/static/images/tree_arrow_down.xcf differ diff --git a/static/images/tree_arrow_down_hover.png b/static/images/tree_arrow_down_hover.png new file mode 100644 index 0000000..d717e6a Binary files /dev/null and b/static/images/tree_arrow_down_hover.png differ diff --git a/static/images/tree_arrow_down_hover.xcf b/static/images/tree_arrow_down_hover.xcf new file mode 100644 index 0000000..89d1f55 Binary files /dev/null and b/static/images/tree_arrow_down_hover.xcf differ diff --git a/static/images/tree_arrow_hover.png b/static/images/tree_arrow_hover.png new file mode 100644 index 0000000..8da975d Binary files /dev/null and b/static/images/tree_arrow_hover.png differ diff --git a/static/images/tree_arrow_hover.xcf b/static/images/tree_arrow_hover.xcf new file mode 100644 index 0000000..7980bda Binary files /dev/null and b/static/images/tree_arrow_hover.xcf differ diff --git a/static/js/Wiki.js b/static/js/Wiki.js index 92e68d9..227b1c7 100644 --- a/static/js/Wiki.js +++ b/static/js/Wiki.js @@ -9,7 +9,6 @@ function Wiki( invoker ) { this.parent_id = getElement( "parent_id" ).value; // id of the notebook containing this one this.startup_notes = new Array(); // map of startup notes: note id to bool this.open_editors = new Array(); // map of open notes: note title to editor - this.all_notes_editor = null; // editor for display of list of all notes this.search_results_editor = null; // editor for display of search results this.invoker = invoker; this.rate_plan = evalJSON( getElement( "rate_plan" ).value ); @@ -19,6 +18,7 @@ function Wiki( invoker ) { this.after_login = getElement( "after_login" ).value; this.signup_plan = getElement( "signup_plan" ).value; this.font_size = null; + this.note_tree = new Note_tree( this, this.notebook_id, this.invoker ); var total_notes_count_node = getElement( "total_notes_count" ); if ( total_notes_count_node ) @@ -312,14 +312,6 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri ); } - var all_notes_link = getElement( "all_notes_link" ); - if ( all_notes_link ) { - connect( all_notes_link, "onclick", function ( event ) { - self.load_editor( "all notes", "null", null, null, getElement( "notes_top" ) ); - event.stop(); - } ); - } - var download_html_link = getElement( "download_html_link" ); if ( download_html_link ) { connect( download_html_link, "onclick", function ( event ) { @@ -398,8 +390,7 @@ Wiki.prototype.create_blank_editor = function ( event ) { var editor = this.create_editor( undefined, undefined, undefined, undefined, undefined, this.notebook.read_write, true, true ); this.increment_total_notes_count(); this.blank_editor_id = editor.id; - - this.add_all_notes_link( editor.id, "" ); + signal( this, "note_added", editor ); } Wiki.prototype.load_editor = function ( note_title, note_id, revision, link, position_after ) { @@ -443,13 +434,6 @@ Wiki.prototype.load_editor = function ( note_title, note_id, revision, link, pos var self = this; if ( pulldown_title || note_id == undefined || note_id == "new" || note_id == "null" ) { // if the note_title corresponds to a "magic" note's title, then dynamically highlight or create the note - if ( note_title == "all notes" ) { - this.invoker.invoke( - "/notebooks/all_notes", "GET", { "notebook_id": this.notebook.object_id }, - function( result ) { self.display_all_notes_list( result ); } - ); - return; - } if ( note_title == "search results" ) { var editor = this.open_editors[ note_title ]; if ( editor ) { @@ -513,14 +497,12 @@ Wiki.prototype.resolve_link = function ( note_title, link, callback ) { if ( link && link.target ) link.removeAttribute( "target" ); - if ( note_title == "all notes" || note_title == "search results" || note_title == "share this notebook" ) { + if ( note_title == "search results" || note_title == "share this notebook" ) { link.href = "/notebooks/" + this.notebook_id + "?" + queryString( [ "title", "note_id" ], [ note_title, "null" ] ); if ( callback ) { - if ( note_title == "all notes" ) - callback( "list of all notes in this notebook" ); if ( note_title == "search results" ) callback( "current search results" ); else @@ -751,7 +733,7 @@ Wiki.prototype.editor_title_changed = function ( editor, old_title, new_title ) if ( new_title != null && !editor.empty() ) { this.open_editors[ new_title ] = editor; - this.add_all_notes_link( editor.id, new_title ); + signal( this, "note_renamed", editor, new_title ); } } @@ -804,7 +786,7 @@ Wiki.prototype.editor_focused = function ( editor, synchronous ) { // if the formerly focused editor is completely empty, then remove it as the user leaves it and switches to this editor if ( this.focused_editor.id == this.blank_editor_id && this.focused_editor.empty() ) { - this.remove_all_notes_link( this.focused_editor.id ); + signal( this, "note_removed", this.focused_editor.id ); this.focused_editor.shutdown(); this.decrement_total_notes_count(); this.display_empty_message(); @@ -1081,7 +1063,7 @@ Wiki.prototype.hide_editor = function ( event, editor ) { // if the editor to hide is completely empty, then simply remove it if ( editor.id == this.blank_editor_id && editor.empty() ) { - this.remove_all_notes_link( editor.id ); + signal( this, "note_removed", editor.id ); editor.shutdown(); this.decrement_total_notes_count(); } else { @@ -1090,10 +1072,9 @@ Wiki.prototype.hide_editor = function ( event, editor ) { this.save_editor( editor ); editor.shutdown(); - Highlight( "all_notes_link" ); } - this.display_empty_message( id == "all_notes" ); + this.display_empty_message(); } event.stop(); @@ -1142,7 +1123,7 @@ Wiki.prototype.delete_editor = function ( event, editor ) { connect( undo_button, "onclick", function ( event ) { self.undelete_editor_via_undo( event, editor, message_div ); } ); } - self.remove_all_notes_link( editor.id ); + signal( self, "note_removed", editor.id ); editor.shutdown(); self.decrement_total_notes_count(); @@ -1176,7 +1157,7 @@ Wiki.prototype.undelete_editor_via_trash = function ( event, editor ) { if ( editor == this.focused_editor ) this.focused_editor = null; - this.remove_all_notes_link( editor.id ); + signal( this, "note_removed", editor.id ); editor.shutdown(); this.decrement_total_notes_count(); @@ -1199,6 +1180,7 @@ Wiki.prototype.undelete_editor_via_undo = function( event, editor, position_afte this.startup_notes[ editor.id ] = true; this.increment_total_notes_count(); this.load_editor( "Note not found.", editor.id, null, null, position_after ); + signal( this, "note_added", editor ); } event.stop(); @@ -1216,6 +1198,7 @@ Wiki.prototype.undelete_editor_via_undelete = function( event, note_id, position this.startup_notes[ note_id ] = true; this.increment_total_notes_count(); this.load_editor( "Note not found.", note_id, null, null, position_after ); + signal( this, "note_removed", editor.id ); event.stop(); } @@ -1371,40 +1354,6 @@ Wiki.prototype.display_search_results = function ( result ) { this.search_results_editor = this.create_editor( "search_results", "

search results

" + list.innerHTML, undefined, undefined, undefined, false, true, true, getElement( "notes_top" ) ); } -Wiki.prototype.display_all_notes_list = function ( result ) { - this.clear_messages(); - this.clear_pulldowns(); - - if ( this.display_empty_message( true ) == true ) - return; - - if ( this.all_notes_editor ) - this.all_notes_editor.shutdown(); - - // build up a list of all notes in this notebook, one link per note - var list = createDOM( "ul", { "id": "notes_list" } ); - if ( this.focused_editor ) { - var focused_title = this.focused_editor.title; - if ( focused_title != "all notes" && focused_title != "search results" && focused_title != "share this notebook" ) - appendChildNodes( list, this.create_all_notes_link( this.focused_editor.id, this.focused_editor.title || "untitled note" ) ); - } - - for ( var i in result.notes ) { - var note_tuple = result.notes[ i ] - var note_id = note_tuple[ 0 ]; - var note_title = note_tuple[ 1 ]; - if ( this.focused_editor && note_id == this.focused_editor.id ) - continue; - if ( !note_title ) - note_title = "untitled note"; - - appendChildNodes( list, this.create_all_notes_link( note_id, note_title ) ); - } - var list_holder = createDOM( "div", {}, list ); - - this.all_notes_editor = this.create_editor( "all_notes", "

all notes

" + list_holder.innerHTML, undefined, undefined, undefined, false, true, true, getElement( "notes_top" ) ); -} - Wiki.prototype.share_notebook = function () { this.clear_pulldowns(); @@ -1792,11 +1741,7 @@ Wiki.prototype.display_empty_message = function ( replace_messages ) { } if ( !replace_messages ) { - var self = this; - this.invoker.invoke( - "/notebooks/all_notes", "GET", { "notebook_id": this.notebook.object_id }, - function( result ) { self.display_all_notes_list( result ); } - ); + // TODO: display a message about the note tree, or some way for the user to get back to their notes return true; } @@ -1837,55 +1782,6 @@ Wiki.prototype.zero_total_notes_count = function () { replaceChildNodes( "total_notes_count", this.total_notes_count ); } -Wiki.prototype.remove_all_notes_link = function ( note_id ) { - if ( !this.all_notes_editor ) return; - - withDocument( this.all_notes_editor.document, function () { - var note_link = getElement( "note_link_" + note_id ); - if ( note_link ) - removeElement( note_link ); - } ); - - this.all_notes_editor.resize(); -} - -Wiki.prototype.add_all_notes_link = function ( note_id, note_title ) { - if ( !this.all_notes_editor ) return; - if ( note_title == "all notes" || note_title == "search results" || note_title == "share this notebook" ) return; - - if ( !note_title || note_title.length == 0 ) - note_title = "untitled note"; - - var self = this; - withDocument( this.all_notes_editor.document, function () { - // if the note link already exists, update its title and bail - var note_link = getElement( "note_link_" + note_id ); - if ( note_link ) { - replaceChildNodes( note_link.firstChild, note_title ); - self.all_notes_editor.resize(); - return; - } - - var notes_list = getElement( "notes_list" ); - if ( !notes_list ) return; - var first_note_link = notes_list.firstChild; - var new_note_link = self.create_all_notes_link( note_id, note_title ); - - if ( first_note_link ) - insertSiblingNodesBefore( first_note_link, new_note_link ); - else - appendChildNodes( notes_list, new_note_link ); - } ); - - this.all_notes_editor.resize(); -} - -Wiki.prototype.create_all_notes_link = function ( note_id, note_title ) { - return createDOM( "li", { "id": "note_link_" + note_id }, - createDOM( "a", { "href": "/notebooks/" + this.notebook_id + "?note_id=" + note_id }, note_title ) - ); -} - Wiki.prototype.start_notebook_rename = function () { this.clear_pulldowns(); @@ -2257,12 +2153,6 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) { // if the note has no destination note id set, try loading the note from the server by title if ( ( id == undefined || id == "new" || id == "null" ) && title.length > 0 ) { - if ( title == "all notes" ) { - this.title_field.value = title; - this.display_summary( title, "list of all notes in this notebook" ); - return; - } - if ( title == "search results" ) { this.title_field.value = title; this.display_summary( title, "current search results" ); @@ -2663,3 +2553,76 @@ File_link_pulldown.prototype.shutdown = function () { this.link.pulldown = null; } +function Note_tree( wiki, notebook_id, invoker ) { + this.wiki = wiki; + this.notebook_id = notebook_id; + this.invoker = invoker; + + // add onclick handlers to the initial note links within the tree + var links = getElementsByTagAndClassName( "a", "note_tree_link", "note_tree_area" ); + + var self = this; + for ( var i in links ) { + var link = links[ i ]; + // TODO: connect expander as well + connect( link, "onclick", function ( event ) { self.link_clicked( event ); } ); + } + + // connect to the wiki note events + connect( wiki, "note_renamed", function ( editor, new_title ) { self.rename_link( editor, new_title ); } ); + connect( wiki, "note_added", function ( editor ) { self.add_link( editor ); } ); + connect( wiki, "note_removed", function ( id ) { self.remove_link( id ); } ); + connect( wiki, "note_updated", function ( editor ) { self.update_child_links( editor ); } ); +} + +Note_tree.prototype.link_clicked = function ( event ) { + var link = event.target(); + var query = parse_query( link ); + var note_id = query[ "note_id" ]; + + if ( !note_id ) + return; + + this.wiki.load_editor( null, note_id ); + event.stop(); +} + +Note_tree.prototype.add_link = function ( editor ) { + // for now, only add startup notes to the note tree + if ( !editor.startup ) + return; + + var expander = createDOM( "div", { "class": "tree_expander" } ); + var link = createDOM( "a", { + "href": "/notebooks/" + this.notebook_id + "?note_id=" + editor.id, + "id": "note_tree_link_" + editor.id, + "class": "note_tree_link" + }, editor.title ); + + appendChildNodes( "note_tree_area_holder", createDOM( + "div", + { "id": "note_tree_item_" + editor.id, "class": "link_area_item" }, + expander, + link + ) ); + + var self = this; + // TODO: connect expander as well + connect( link, "onclick", function ( event ) { self.link_clicked( event ); } ); +} + +Note_tree.prototype.remove_link = function ( id ) { + removeElement( "note_tree_item_" + id ); +} + +Note_tree.prototype.rename_link = function ( editor, new_title ) { +} + +Note_tree.prototype.expand_link = function ( id ) { +} + +Note_tree.prototype.collapse_link = function ( id ) { +} + +Note_tree.prototype.update_child_links = function ( editor ) { +} diff --git a/view/Link_area.py b/view/Link_area.py index 92c5b81..56f4180 100644 --- a/view/Link_area.py +++ b/view/Link_area.py @@ -4,7 +4,7 @@ from Search_form import Search_form class Link_area( Div ): - def __init__( self, notebooks, notebook, total_notes_count, parent_id, notebook_path, user ): + def __init__( self, notebooks, notebook, parent_id, notebook_path, user ): linked_notebooks = [ nb for nb in notebooks if ( nb.read_write or not nb.name.startswith( u"Luminotes" ) ) and nb.name not in ( u"trash" ) and @@ -20,19 +20,6 @@ class Link_area( Div ): Search_form(), class_ = u"link_area_item", ), - ( parent_id is None ) and Div( - A( - u"all notes", - href = u"#", - id = u"all_notes_link", - title = u"View a list of all notes in this notebook.", - ), - Span( - Span( total_notes_count, id = u"total_notes_count" ), u"total", - class_ = u"small_text", - ), - class_ = u"link_area_item", - ) or None, ( notebook.name != u"Luminotes" ) and Div( A( @@ -156,7 +143,7 @@ class Link_area( Div ): Div( id = u"storage_usage_area", ), - id = u"link_area_wrapper", + id = u"link_area_holder", ), id = u"link_area", ) diff --git a/view/Main_page.py b/view/Main_page.py index e2f3d5c..53d9e3c 100644 --- a/view/Main_page.py +++ b/view/Main_page.py @@ -2,6 +2,7 @@ from cgi import escape from Page import Page from Header import Header from Tags import Link, Input, Div, Span, H2, H4, A, Br, Strong, Script, Img +from Note_tree_area import Note_tree_area from Link_area import Link_area from Toolbar import Toolbar from Json import Json @@ -134,11 +135,17 @@ class Main_page( Page ): Header( user, header_notebook, login_url, logout_url, header_note_title ), Div( Div( - Br(), - Toolbar( hide_toolbar = parent_id or not notebook.read_write ), - id = u"toolbar_area", + Note_tree_area( + Toolbar( + hide_toolbar = parent_id or not notebook.read_write + ), + notebook, + startup_notes, # TODO: pass root_notes, not startup_notes + total_notes_count, + ), + id = u"left_area", ), - Link_area( notebooks, notebook, total_notes_count, parent_id, notebook_path, user ), + Link_area( notebooks, notebook, parent_id, notebook_path, user ), Div( Rounded_div( ( notebook.name == u"trash" ) and u"trash_notebook" or u"current_notebook", diff --git a/view/Note_tree_area.py b/view/Note_tree_area.py new file mode 100644 index 0000000..c8e5c1f --- /dev/null +++ b/view/Note_tree_area.py @@ -0,0 +1,31 @@ +from Tags import Div, Span, H4, A + + +class Note_tree_area( Div ): + def __init__( self, toolbar, notebook, root_notes, total_notes_count ): + Div.__init__( + self, + toolbar, + Div( + H4( u"notes", + Span( + Span( total_notes_count, id = u"total_notes_count" ), u"total", + class_ = u"small_text link_area_item", + ), + id = u"note_tree_area_title", + ), + [ Div( + Div( class_ = u"tree_expander" ), + A( + note.title, + href = u"/notebooks/%s?note_id=%s" % ( notebook.object_id, note.object_id ), + id = u"note_tree_link_" + note.object_id, + class_ = u"note_tree_link", + ), + id = u"note_tree_item_" + note.object_id, + class_ = u"link_area_item", + ) for note in root_notes ], + id = u"note_tree_area_holder", + ), + id = u"note_tree_area", + )