2007-09-06 21:36:39 +00:00
function Wiki ( invoker ) {
2007-07-16 20:22:38 +00:00
this . next _id = null ;
this . focused _editor = null ;
this . blank _editor _id = null ;
this . notebook = null ;
this . notebook _id = getElement ( "notebook_id" ) . value ;
2007-08-29 00:50:46 +00:00
this . parent _id = getElement ( "parent_id" ) . value ; // id of the notebook containing this one
2007-07-16 20:22:38 +00:00
this . read _write = false ;
2007-08-16 01:29:18 +00:00
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
2007-09-11 19:19:03 +00:00
this . all _notes _editor = null ; // editor for display of list of all notes
2007-09-10 20:04:46 +00:00
this . search _results _editor = null ; // editor for display of search results
2007-09-06 21:36:39 +00:00
this . invoker = invoker ;
2007-08-17 22:26:02 +00:00
this . search _titles _only = true ;
2007-09-20 20:36:19 +00:00
this . rate _plan = null ;
2007-09-20 22:59:00 +00:00
this . storage _usage _high = false ;
2007-07-16 20:22:38 +00:00
2007-08-07 01:48:43 +00:00
connect ( this . invoker , "error_message" , this , "display_error" ) ;
2007-07-16 20:22:38 +00:00
connect ( "search_form" , "onsubmit" , this , "search" ) ;
2007-08-17 22:26:02 +00:00
connect ( "search_button" , "onclick" , this , "toggle_search_options" ) ;
2007-09-05 23:11:19 +00:00
connect ( "html" , "onclick" , this , "background_clicked" ) ;
2007-07-16 20:22:38 +00:00
// get info on the requested notebook (if any)
var self = this ;
if ( this . notebook _id ) {
this . invoker . invoke (
"/notebooks/contents" , "GET" , {
2007-07-28 04:22:44 +00:00
"notebook_id" : this . notebook _id ,
2007-07-31 22:53:57 +00:00
"note_id" : getElement ( "note_id" ) . value ,
"revision" : getElement ( "revision" ) . value
2007-07-16 20:22:38 +00:00
} ,
function ( result ) { self . populate ( result ) ; }
) ;
2007-08-27 22:37:22 +00:00
var include _startup _notes = false ;
} else {
var include _startup _notes = true ;
2007-07-16 20:22:38 +00:00
}
// get info on the current user (logged-in or anonymous)
2007-08-27 22:37:22 +00:00
this . invoker . invoke ( "/users/current" , "GET" , {
"include_startup_notes" : include _startup _notes
} ,
2007-07-16 20:22:38 +00:00
function ( result ) { self . display _user ( result ) ; }
) ;
}
Wiki . prototype . update _next _id = function ( result ) {
this . next _id = result . next _id ;
}
Wiki . prototype . display _user = function ( result ) {
// if no notebook id was requested, then just display the user's default notebook
if ( ! this . notebook _id ) {
this . notebook _id = result . notebooks [ 0 ] . object _id ;
2007-08-27 22:37:22 +00:00
this . populate ( { "notebook" : result . notebooks [ 0 ] , "startup_notes" : result . startup _notes } ) ;
2007-07-16 20:22:38 +00:00
}
2007-08-30 23:57:56 +00:00
var user _span = createDOM ( "span" ) ;
replaceChildNodes ( "user_area" , user _span ) ;
// if not logged in, display a login link
if ( result . user . username == "anonymous" && result . login _url ) {
appendChildNodes ( user _span , createDOM ( "a" , { "href" : result . login _url , "id" : "login_link" } , "login" ) ) ;
2007-07-16 20:22:38 +00:00
return ;
2007-08-30 23:57:56 +00:00
}
2007-07-16 20:22:38 +00:00
2007-08-28 20:36:33 +00:00
// display links for current notebook and a list of all notebooks that the user has access to
2007-08-30 23:57:56 +00:00
var notebooks _span = createDOM ( "span" ) ;
replaceChildNodes ( "notebooks_area" , notebooks _span ) ;
2007-08-28 20:36:33 +00:00
2007-09-27 00:26:51 +00:00
appendChildNodes ( notebooks _span , createDOM ( "h4" , "notebooks" ) ) ;
2007-07-16 20:22:38 +00:00
for ( var i in result . notebooks ) {
var notebook = result . notebooks [ i ] ;
2007-08-28 20:36:33 +00:00
2007-08-30 19:08:37 +00:00
if ( notebook . name == "Luminotes" )
continue ;
2007-08-28 20:57:08 +00:00
var div _class = "link_area_item" ;
if ( notebook . object _id == this . notebook _id )
div _class += " current_notebook_name" ;
2007-08-28 20:36:33 +00:00
2007-08-30 23:57:56 +00:00
appendChildNodes ( notebooks _span , createDOM ( "div" , {
2007-08-28 20:36:33 +00:00
"class" : div _class
} , createDOM ( "a" , {
2007-08-29 01:18:06 +00:00
"href" : "/notebooks/" + notebook . object _id ,
2007-08-28 20:36:33 +00:00
"id" : "notebook_" + notebook . object _id
} , notebook . name ) ) ) ;
2007-07-16 20:22:38 +00:00
}
2007-09-20 20:36:19 +00:00
this . rate _plan = result . rate _plan ;
this . display _storage _usage ( result . user . storage _bytes ) ;
2007-07-16 20:22:38 +00:00
// display the name of the logged in user and a logout link
2007-08-30 23:57:56 +00:00
appendChildNodes ( user _span , "logged in as " + result . user . username ) ;
appendChildNodes ( user _span , " | " ) ;
appendChildNodes ( user _span , createDOM ( "a" , { "href" : result . http _url + "/" , "id" : "logout_link" } , "logout" ) ) ;
2007-07-16 20:22:38 +00:00
var self = this ;
connect ( "logout_link" , "onclick" , function ( event ) {
self . save _editor ( null , true ) ;
self . invoker . invoke ( "/users/logout" , "POST" ) ;
event . stop ( ) ;
} ) ;
}
2007-09-20 20:36:19 +00:00
Wiki . prototype . display _storage _usage = function ( storage _bytes ) {
2007-09-20 22:26:43 +00:00
if ( ! storage _bytes )
return ;
2007-09-20 20:36:19 +00:00
// display the user's current storage usage
var MEGABYTE = 1024 * 1024 ;
function bytes _to _megabytes ( storage _bytes ) {
return Math . round ( storage _bytes / MEGABYTE ) ;
}
var quota _bytes = this . rate _plan . storage _quota _bytes || 0 ;
var usage _percent = Math . round ( storage _bytes / quota _bytes * 100.0 ) ;
2007-09-20 23:30:09 +00:00
if ( usage _percent > 90 ) {
2007-09-20 20:36:19 +00:00
var storage _usage _class = "storage_usage_high" ;
2007-09-20 23:25:59 +00:00
if ( this . storage _usage _high == false )
this . display _message ( "You are currently using " + usage _percent + "% of your available storage space. Please delete some notes, empty the trash, or upgrade your account." ) ;
2007-09-20 22:59:00 +00:00
this . storage _usage _high = true ;
} else if ( usage _percent > 75 ) {
2007-09-20 20:36:19 +00:00
var storage _usage _class = "storage_usage_medium" ;
2007-09-20 22:59:00 +00:00
this . storage _usage _high = false ;
} else {
2007-09-20 20:36:19 +00:00
var storage _usage _class = "storage_usage_low" ;
2007-09-20 22:59:00 +00:00
this . storage _usage _high = false ;
}
2007-09-20 20:36:19 +00:00
replaceChildNodes (
"storage_usage_area" ,
createDOM ( "div" , { "class" : storage _usage _class } ,
bytes _to _megabytes ( storage _bytes ) + " MB (" + usage _percent + "%) of " + bytes _to _megabytes ( quota _bytes ) + " MB used" )
) ;
}
2007-07-16 20:22:38 +00:00
Wiki . prototype . populate = function ( result ) {
this . notebook = result . notebook ;
var self = this ;
2007-08-28 20:57:08 +00:00
var header _area = getElement ( "notebook_header_area" ) ;
2007-08-29 00:50:46 +00:00
replaceChildNodes ( header _area , createDOM ( "b" , { } , this . notebook . name ) ) ;
2007-09-01 21:06:37 +00:00
2007-08-29 00:50:46 +00:00
if ( this . parent _id ) {
2007-09-01 21:06:37 +00:00
appendChildNodes ( header _area , createDOM ( "span" , { } , ": " ) ) ;
var empty _trash _link = createDOM ( "a" , { "href" : location . href } , "empty trash" ) ;
appendChildNodes ( header _area , empty _trash _link ) ;
connect ( empty _trash _link , "onclick" , function ( event ) { try { self . delete _all _editors ( event ) ; } catch ( e ) { alert ( e ) ; } } ) ;
2007-08-29 00:50:46 +00:00
appendChildNodes ( header _area , createDOM ( "span" , { } , " | " ) ) ;
appendChildNodes ( header _area , createDOM ( "a" , { "href" : "/notebooks/" + this . parent _id } , "return to notebook" ) ) ;
}
2007-08-28 20:57:08 +00:00
2007-08-07 01:48:43 +00:00
var span = createDOM ( "span" ) ;
2007-08-28 20:36:33 +00:00
replaceChildNodes ( "this_notebook_area" , span ) ;
2007-08-07 01:48:43 +00:00
2007-09-27 00:26:51 +00:00
appendChildNodes ( span , createDOM ( "h4" , "this notebook" ) ) ;
2007-09-04 21:37:48 +00:00
if ( ! this . parent _id ) {
appendChildNodes ( span , createDOM ( "div" , { "class" : "link_area_item" } ,
createDOM ( "a" , { "href" : location . href , "id" : "all_notes_link" , "title" : "View a list of all notes in this notebook." } , "all notes" )
) ) ;
}
2007-08-28 20:36:33 +00:00
appendChildNodes ( span , createDOM ( "div" , { "class" : "link_area_item" } ,
createDOM ( "a" , { "href" : "/notebooks/download_html/" + this . notebook . object _id , "id" : "download_html_link" , "title" : "Download a stand-alone copy of the entire wiki notebook." } , "download as html" )
) ) ;
2007-08-07 01:48:43 +00:00
2007-07-16 20:22:38 +00:00
if ( this . notebook . read _write ) {
this . read _write = true ;
removeElementClass ( "toolbar" , "undisplayed" ) ;
2007-08-28 20:36:33 +00:00
if ( this . notebook . trash ) {
appendChildNodes ( span , createDOM ( "div" , { "class" : "link_area_item" } ,
2007-08-28 20:57:08 +00:00
createDOM ( "a" , {
2007-08-29 00:50:46 +00:00
"href" : "/notebooks/" + this . notebook . trash . object _id + "?parent_id=" + this . notebook . object _id ,
2007-08-28 20:57:08 +00:00
"id" : "trash_link" ,
"title" : "Look here for notes you've deleted."
} , "trash" )
) ) ;
} else if ( this . notebook . name == "trash" ) {
appendChildNodes ( span , createDOM ( "div" , { "class" : "link_area_item current_trash_notebook_name" } ,
createDOM ( "a" , {
2007-08-29 00:50:46 +00:00
"href" : location . href ,
2007-08-28 20:57:08 +00:00
"id" : "trash_link" ,
"title" : "Look here for notes you've deleted."
} , "trash" )
2007-08-28 20:36:33 +00:00
) ) ;
2007-08-28 20:57:08 +00:00
var header _area = getElement ( "notebook_header_area" )
removeElementClass ( header _area , "current_notebook_name" ) ;
addElementClass ( header _area , "current_trash_notebook_name" ) ;
var border = getElement ( "notebook_border" )
removeElementClass ( border , "current_notebook_name" ) ;
addElementClass ( border , "current_trash_notebook_name" ) ;
2007-08-28 20:36:33 +00:00
}
2007-08-07 01:48:43 +00:00
2007-07-16 20:22:38 +00:00
connect ( window , "onunload" , function ( event ) { self . editor _focused ( null , true ) ; } ) ;
connect ( "bold" , "onclick" , function ( event ) { self . toggle _button ( event , "bold" ) ; } ) ;
connect ( "italic" , "onclick" , function ( event ) { self . toggle _button ( event , "italic" ) ; } ) ;
2007-08-27 21:01:42 +00:00
connect ( "underline" , "onclick" , function ( event ) { self . toggle _button ( event , "underline" ) ; } ) ;
2007-07-16 20:22:38 +00:00
connect ( "title" , "onclick" , function ( event ) { self . toggle _button ( event , "title" , "h3" ) ; } ) ;
connect ( "insertUnorderedList" , "onclick" , function ( event ) { self . toggle _button ( event , "insertUnorderedList" ) ; } ) ;
connect ( "insertOrderedList" , "onclick" , function ( event ) { self . toggle _button ( event , "insertOrderedList" ) ; } ) ;
connect ( "createLink" , "onclick" , this , "toggle_link_button" ) ;
2007-07-17 01:21:31 +00:00
connect ( "newNote" , "onclick" , this , "create_blank_editor" ) ;
2007-07-16 20:22:38 +00:00
// grab the next available object id
this . invoker . invoke ( "/next_id" , "POST" , null ,
function ( result ) { self . update _next _id ( result ) ; }
) ;
}
2007-08-07 01:48:43 +00:00
var self = this ;
2007-09-04 21:37:48 +00:00
if ( ! this . parent _id ) {
connect ( "all_notes_link" , "onclick" , function ( event ) {
2007-09-07 23:58:03 +00:00
self . load _editor ( "all notes" , "null" ) ;
2007-09-04 21:37:48 +00:00
event . stop ( ) ;
} ) ;
}
2007-08-07 01:48:43 +00:00
connect ( "download_html_link" , "onclick" , function ( event ) {
self . save _editor ( null , true ) ;
} ) ;
2007-07-17 01:21:31 +00:00
// create an editor for each startup note in the received notebook, focusing the first one
2007-08-07 01:48:43 +00:00
var focus = true ;
2007-08-27 22:37:22 +00:00
for ( var i in result . startup _notes ) {
var note = result . startup _notes [ i ] ;
2007-07-17 01:21:31 +00:00
if ( ! note ) continue ;
this . startup _notes [ note . object _id ] = true ;
2007-07-28 04:22:44 +00:00
// don't actually create an editor if a particular note was provided in the result
if ( ! result . note ) {
2007-09-05 18:49:28 +00:00
var editor = this . create _editor ( note . object _id , note . contents , note . deleted _from , note . revisions _list , undefined , this . read _write , false , focus ) ;
this . open _editors [ note . title ] = editor ;
2007-08-07 01:48:43 +00:00
focus = false ;
2007-07-28 04:22:44 +00:00
}
2007-07-16 20:22:38 +00:00
}
2007-07-28 04:22:44 +00:00
// if one particular note was provided, then just display an editor for that note
2007-07-31 22:53:57 +00:00
var read _write = this . read _write ;
if ( getElement ( "revision" ) . value ) read _write = false ;
2007-07-28 04:22:44 +00:00
if ( result . note )
2007-09-26 23:49:27 +00:00
this . create _editor (
result . note . object _id ,
result . note . contents || getElement ( "note_contents" ) . value ,
result . note . deleted _from ,
result . note . revisions _list ,
undefined , read _write , false , true
) ;
2007-08-16 23:27:29 +00:00
2007-09-05 18:49:28 +00:00
if ( result . startup _notes . length == 0 && ! result . note )
this . display _empty _message ( ) ;
2007-07-16 20:22:38 +00:00
}
2007-09-05 23:11:19 +00:00
Wiki . prototype . background _clicked = function ( event ) {
if ( ! hasElementClass ( event . target ( ) , "pulldown_checkbox" ) )
this . clear _pulldowns ( ) ;
}
2007-07-16 20:22:38 +00:00
Wiki . prototype . create _blank _editor = function ( event ) {
if ( event ) event . stop ( ) ;
2007-08-30 20:39:41 +00:00
// if we're within the trash, don't allow new note creation
if ( this . notebook . name == "trash" ) {
this . display _error ( "You can't create notes in the trash." ) ;
return ;
}
2007-07-16 20:22:38 +00:00
// if there is already a blank editor, then highlight it and bail
if ( this . blank _editor _id != null ) {
2007-07-17 01:21:31 +00:00
var blank _iframe _id = "note_" + this . blank _editor _id ;
2007-07-16 20:22:38 +00:00
var iframe = getElement ( blank _iframe _id ) ;
if ( iframe && iframe . editor . empty ( ) ) {
iframe . editor . highlight ( ) ;
return ;
}
}
2007-09-05 18:49:28 +00:00
var editor = this . create _editor ( undefined , undefined , undefined , undefined , undefined , this . read _write , true , true ) ;
this . blank _editor _id = editor . id ;
2007-07-16 20:22:38 +00:00
}
2007-08-24 20:50:07 +00:00
Wiki . prototype . load _editor = function ( note _title , note _id , revision , link ) {
2007-09-02 03:51:31 +00:00
if ( this . notebook . name == "trash" && ! revision ) {
2007-08-30 20:39:41 +00:00
this . display _message ( "If you'd like to use this note, try undeleting it first." ) ;
return ;
}
2007-08-15 00:18:30 +00:00
// if a link is given with an open link pulldown, then ignore the note title given and use the
// one from the pulldown instead
2007-08-16 19:55:11 +00:00
if ( link ) {
var pulldown = link . pulldown ;
var pulldown _title = undefined ;
if ( pulldown ) {
pulldown _title = strip ( pulldown . title _field . value ) ;
if ( pulldown _title )
note _title = pulldown _title ;
else
pulldown . title _field . value = note _title ;
}
2007-08-15 00:18:30 +00:00
2007-08-23 06:45:42 +00:00
// if the title looks like a URL, then make it a link to an external site
if ( /^\w+:\/\// . test ( note _title ) ) {
link . target = "_new" ;
link . href = note _title ;
window . open ( link . href ) ;
return
}
link . removeAttribute ( "target" ) ;
2007-08-16 22:27:58 +00:00
}
2007-09-06 08:27:41 +00:00
// if the note corresponding to the link's id is already open, highlight it and bail, but only if
// we didn't pull a title from an open link pulldown
if ( ! pulldown _title ) {
if ( revision )
var iframe = getElement ( "note_" + note _id + " " + revision ) ;
else
var iframe = getElement ( "note_" + note _id ) ;
if ( iframe ) {
iframe . editor . highlight ( ) ;
if ( link )
link . href = "/notebooks/" + this . notebook _id + "?note_id=" + note _id ;
return ;
}
2007-08-16 01:29:18 +00:00
}
2007-09-07 23:46:01 +00:00
// if there's not a valid destination note id, then load by title instead of by id
2007-08-16 01:29:18 +00:00
var self = this ;
2007-09-07 23:46:01 +00:00
if ( pulldown _title || note _id == undefined || note _id == "new" || note _id == "null" ) {
2007-09-10 20:04:46 +00:00
// if the note_title corresponds to a "magic" note's title, then dynamically highlight or create the note
2007-09-07 23:46:01 +00:00
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 ) ; }
) ;
2007-09-05 23:44:34 +00:00
return ;
}
2007-09-10 20:04:46 +00:00
if ( note _title == "search results" ) {
var editor = this . open _editors [ note _title ] ;
if ( editor ) {
editor . highlight ( ) ;
return ;
}
this . display _search _results ( ) ;
return ;
}
2007-09-05 23:44:34 +00:00
2007-09-05 19:54:04 +00:00
// but if the note corresponding to the link's title is already open, highlight it and bail
if ( ! revision ) {
var editor = this . open _editors [ note _title ] ;
if ( editor ) {
editor . highlight ( ) ;
if ( link )
link . href = "/notebooks/" + this . notebook _id + "?note_id=" + editor . id ;
return ;
}
}
2007-08-14 04:13:49 +00:00
this . invoker . invoke (
"/notebooks/load_note_by_title" , "GET" , {
"notebook_id" : this . notebook _id ,
"note_title" : note _title ,
"revision" : revision
} ,
2007-08-24 20:50:07 +00:00
function ( result ) { self . parse _loaded _editor ( result , note _title , revision , link ) ; }
2007-08-14 04:13:49 +00:00
) ;
return ;
}
2007-07-16 20:22:38 +00:00
this . invoker . invoke (
2007-07-17 01:21:31 +00:00
"/notebooks/load_note" , "GET" , {
2007-07-16 20:22:38 +00:00
"notebook_id" : this . notebook _id ,
2007-07-31 22:53:57 +00:00
"note_id" : note _id ,
"revision" : revision
2007-07-16 20:22:38 +00:00
} ,
2007-08-24 20:50:07 +00:00
function ( result ) { self . parse _loaded _editor ( result , note _title , revision , link ) ; }
2007-07-16 20:22:38 +00:00
) ;
}
2007-08-16 01:29:18 +00:00
Wiki . prototype . resolve _link = function ( note _title , link , callback ) {
2007-08-16 22:27:58 +00:00
// if the title looks like a URL, then make it a link to an external site
if ( /^\w+:\/\// . test ( note _title ) ) {
link . target = "_new" ;
link . href = note _title ;
if ( callback ) callback ( "web link" ) ;
2007-09-07 23:03:12 +00:00
return ;
2007-08-16 22:27:58 +00:00
}
link . removeAttribute ( "target" ) ;
2007-09-10 20:04:46 +00:00
if ( note _title == "all notes" || note _title == "search results" ) {
2007-09-07 23:03:12 +00:00
link . href = "/notebooks/" + this . notebook _id + "?" + queryString (
[ "title" , "note_id" ] ,
[ note _title , "null" ]
) ;
2007-09-10 20:04:46 +00:00
if ( callback ) {
if ( note _title == "all notes" )
callback ( "list of all notes in this notebook" ) ;
else
callback ( "current search results" ) ;
}
2007-09-07 23:03:12 +00:00
return ;
}
2007-08-16 01:29:18 +00:00
var id = parse _query ( link ) . note _id ;
// if the link already has a valid-looking id, it's already resolved, so bail
2007-08-16 22:27:58 +00:00
if ( ! callback && id != undefined && id != "new" && id != "null" )
2007-08-16 01:29:18 +00:00
return ;
2007-07-16 20:22:38 +00:00
2007-08-14 04:13:49 +00:00
if ( note _title . length == 0 )
return ;
2007-08-16 01:29:18 +00:00
// if the note corresponding to the link's title is already open, resolve the link and bail
var editor = this . open _editors [ note _title ] ;
if ( editor ) {
link . href = "/notebooks/" + this . notebook _id + "?note_id=" + editor . id ;
if ( callback )
callback ( editor . contents ( ) ) ;
return ;
}
2007-08-14 04:13:49 +00:00
var self = this ;
2007-07-16 20:22:38 +00:00
this . invoker . invoke (
2007-08-16 01:29:18 +00:00
"/notebooks/" + ( callback ? "load_note_by_title" : "lookup_note_id" ) , "GET" , {
2007-07-16 20:22:38 +00:00
"notebook_id" : this . notebook _id ,
2007-07-17 01:21:31 +00:00
"note_title" : note _title
2007-07-16 20:22:38 +00:00
} ,
2007-08-14 04:13:49 +00:00
function ( result ) {
2007-08-16 01:29:18 +00:00
if ( result && ( result . note || result . note _id ) ) {
link . href = "/notebooks/" + self . notebook _id + "?note_id=" + ( result . note ? result . note . object _id : result . note _id ) ;
} else {
link . href = "/notebooks/" + self . notebook _id + "?" + queryString (
[ "title" , "note_id" ] ,
[ note _title , "null" ]
) ;
}
if ( callback )
callback ( ( result && result . note ) ? result . note . contents : null ) ;
2007-08-14 04:13:49 +00:00
}
2007-07-16 20:22:38 +00:00
) ;
}
2007-08-24 20:50:07 +00:00
Wiki . prototype . parse _loaded _editor = function ( result , note _title , revision , link ) {
2007-07-17 01:21:31 +00:00
if ( result . note ) {
2007-07-31 22:53:57 +00:00
var id = result . note . object _id ;
if ( revision ) id += " " + revision ;
2007-07-17 01:21:31 +00:00
var note _text = result . note . contents ;
2007-08-07 01:48:43 +00:00
var deleted _from = result . note . deleted ;
2007-07-26 23:02:24 +00:00
var revisions _list = result . note . revisions _list ;
2007-07-16 20:22:38 +00:00
} else {
var id = null ;
2007-07-17 01:21:31 +00:00
var note _text = "<h3>" + note _title ;
2007-08-07 01:48:43 +00:00
var deleted _from = null ;
2007-07-26 23:02:24 +00:00
var revisions _list = new Array ( ) ;
2007-07-16 20:22:38 +00:00
}
2007-07-31 22:53:57 +00:00
if ( revision )
var read _write = false ; // show previous revisions as read-only
else
var read _write = this . read _write ;
2007-09-05 18:49:28 +00:00
var editor = this . create _editor ( id , note _text , deleted _from , revisions _list , note _title , read _write , true , false ) ;
id = editor . id ;
2007-08-14 20:34:31 +00:00
// if a link that launched this editor was provided, update it with the created note's id
if ( link && id )
2007-08-15 00:18:30 +00:00
link . href = "/notebooks/" + this . notebook _id + "?note_id=" + id ;
2007-07-16 20:22:38 +00:00
}
2007-08-24 20:50:07 +00:00
Wiki . prototype . create _editor = function ( id , note _text , deleted _from , revisions _list , note _title , read _write , highlight , focus ) {
2007-07-16 20:22:38 +00:00
var self = this ;
if ( isUndefinedOrNull ( id ) ) {
if ( this . read _write ) {
id = this . next _id ;
this . invoker . invoke ( "/next_id" , "POST" , null ,
function ( result ) { self . update _next _id ( result ) ; }
) ;
} else {
id = 0 ;
}
}
2007-07-31 22:53:57 +00:00
// for read-only notes within read-write notebooks, tack the revision timestamp onto the start of the note text
if ( ! read _write && this . read _write && revisions _list && revisions _list . length ) {
var short _revision = this . brief _revision ( revisions _list [ revisions _list . length - 1 ] ) ;
2007-09-27 00:26:51 +00:00
note _text = "<p>Previous revision from " + short _revision + "</p>" + note _text ;
2007-07-31 22:53:57 +00:00
}
2007-07-17 01:21:31 +00:00
var startup = this . startup _notes [ id ] ;
2007-09-05 18:53:42 +00:00
var editor = new Editor ( id , this . notebook _id , note _text , deleted _from , revisions _list , read _write , startup , highlight , focus ) ;
2007-07-16 20:22:38 +00:00
if ( this . read _write ) {
connect ( editor , "state_changed" , this , "editor_state_changed" ) ;
2007-08-16 01:29:18 +00:00
connect ( editor , "title_changed" , this , "editor_title_changed" ) ;
2007-07-16 20:22:38 +00:00
connect ( editor , "key_pressed" , this , "editor_key_pressed" ) ;
connect ( editor , "delete_clicked" , function ( event ) { self . delete _editor ( event , editor ) } ) ;
2007-08-07 01:48:43 +00:00
connect ( editor , "undelete_clicked" , function ( event ) { self . undelete _editor _via _trash ( event , editor ) } ) ;
2007-07-26 01:18:41 +00:00
connect ( editor , "changes_clicked" , function ( event ) { self . toggle _editor _changes ( event , editor ) } ) ;
2007-07-16 20:22:38 +00:00
connect ( editor , "options_clicked" , function ( event ) { self . toggle _editor _options ( event , editor ) } ) ;
connect ( editor , "focused" , this , "editor_focused" ) ;
}
connect ( editor , "load_editor" , this , "load_editor" ) ;
2007-08-14 04:13:49 +00:00
connect ( editor , "resolve_link" , this , "resolve_link" ) ;
2007-07-16 20:22:38 +00:00
connect ( editor , "hide_clicked" , function ( event ) { self . hide _editor ( event , editor ) } ) ;
connect ( editor , "submit_form" , function ( url , form ) {
self . invoker . invoke ( url , "POST" , null , null , form ) ;
} ) ;
2007-09-05 18:49:28 +00:00
return editor ;
2007-07-16 20:22:38 +00:00
}
Wiki . prototype . editor _state _changed = function ( editor ) {
this . update _toolbar ( ) ;
2007-08-14 04:13:49 +00:00
this . display _link _pulldown ( editor ) ;
}
2007-08-16 01:29:18 +00:00
Wiki . prototype . editor _title _changed = function ( editor , old _title , new _title ) {
delete this . open _editors [ old _title ] ;
if ( new _title != null )
this . open _editors [ new _title ] = editor ;
}
2007-08-30 20:17:10 +00:00
Wiki . prototype . display _link _pulldown = function ( editor , link ) {
2007-09-04 21:37:48 +00:00
if ( ! editor . read _write ) {
this . clear _pulldowns ( ) ;
return ;
}
2007-08-30 20:17:10 +00:00
if ( ! link )
link = editor . find _link _at _cursor ( ) ;
2007-08-14 04:13:49 +00:00
2007-09-11 00:10:44 +00:00
// if there's no link at the current cursor location, or there is a link but it was just started,
// bail
if ( ! link || link == editor . link _started ) {
2007-08-14 04:13:49 +00:00
this . clear _pulldowns ( ) ;
return ;
}
2007-08-16 01:29:18 +00:00
var pulldown = link . pulldown ;
2007-08-14 04:13:49 +00:00
if ( pulldown )
pulldown . update _position ( ) ;
// if the cursor is now on a link, display a link pulldown if there isn't already one open
2007-08-29 23:54:14 +00:00
if ( link _title ( link ) . length > 0 ) {
2007-08-14 04:13:49 +00:00
if ( ! pulldown ) {
this . clear _pulldowns ( ) ;
new Link _pulldown ( this , this . notebook _id , this . invoker , editor , link ) ;
}
}
2007-07-16 20:22:38 +00:00
}
Wiki . prototype . editor _focused = function ( editor , fire _and _forget ) {
if ( editor )
2007-07-17 01:21:31 +00:00
addElementClass ( editor . iframe , "focused_note_frame" ) ;
2007-07-16 20:22:38 +00:00
if ( this . focused _editor && this . focused _editor != editor ) {
2007-08-14 04:13:49 +00:00
this . clear _pulldowns ( ) ;
2007-07-17 01:21:31 +00:00
removeElementClass ( this . focused _editor . iframe , "focused_note_frame" ) ;
2007-07-16 20:22:38 +00:00
// 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 . empty ( ) ) {
this . focused _editor . shutdown ( ) ;
2007-09-05 18:49:28 +00:00
this . display _empty _message ( ) ;
2007-07-16 20:22:38 +00:00
} else {
// when switching editors, save the one being left
this . save _editor ( null , fire _and _forget ) ;
}
}
this . focused _editor = editor ;
}
Wiki . prototype . editor _key _pressed = function ( editor , event ) {
var code = event . key ( ) . code ;
if ( event . modifier ( ) . ctrl ) {
// ctrl-backtick: alert with frame HTML contents (temporary for debugging)
if ( code == 192 || code == 96 ) {
2007-09-07 23:03:12 +00:00
alert ( editor . contents ( ) ) ;
2007-07-16 20:22:38 +00:00
event . stop ( ) ;
// ctrl-b: bold
} else if ( code == 66 ) {
this . toggle _button ( event , "bold" ) ;
// ctrl-i: italic
} else if ( code == 73 ) {
this . toggle _button ( event , "italic" ) ;
2007-08-27 21:01:42 +00:00
// ctrl-u: underline
} else if ( code == 85 ) {
this . toggle _button ( event , "underline" ) ;
2007-07-16 20:22:38 +00:00
// ctrl-t: title
} else if ( code == 84 ) {
this . toggle _button ( event , "title" , "h3" ) ;
2007-08-27 21:09:39 +00:00
// ctrl-period: unordered list
} else if ( code == 190 ) {
2007-07-16 20:22:38 +00:00
this . toggle _button ( event , "insertUnorderedList" ) ;
// ctrl-n: ordered list
} else if ( code == 49 ) {
this . toggle _button ( event , "insertOrderedList" ) ;
2007-07-18 20:28:42 +00:00
// ctrl-l: make a note link
} else if ( code == 76 ) {
2007-07-16 20:22:38 +00:00
this . toggle _link _button ( event ) ;
2007-07-17 01:21:31 +00:00
// ctrl-n: new note
2007-07-16 20:22:38 +00:00
} else if ( code == 78 ) {
this . create _blank _editor ( event ) ;
2007-07-17 01:21:31 +00:00
// ctrl-h: hide note
2007-07-16 20:22:38 +00:00
} else if ( code == 72 ) {
2007-09-05 18:57:50 +00:00
if ( ! editor . deleted _from )
this . hide _editor ( event ) ;
2007-07-17 01:21:31 +00:00
// ctrl-d: delete note
2007-07-16 20:22:38 +00:00
} else if ( code == 68 ) {
this . delete _editor ( event ) ;
}
// IE: hitting space or tab while making a link shouldn't end the link
} else if ( ( code == 32 || code == 9 ) && editor . document . selection && editor . state _enabled ( "createLink" ) ) {
var range = editor . document . selection . createRange ( ) ;
var text = range . parentElement ( ) . firstChild ;
text . nodeValue += " " ;
event . stop ( ) ;
}
}
Wiki . prototype . toggle _button = function ( event , button _id , state _name ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
2007-07-31 22:53:57 +00:00
if ( this . focused _editor && this . focused _editor . read _write ) {
2007-07-16 20:22:38 +00:00
this . focused _editor . focus ( ) ;
this . focused _editor . exec _command ( state _name || button _id ) ;
this . focused _editor . resize ( ) ;
this . update _button ( button _id , state _name ) ;
}
event . stop ( ) ;
}
Wiki . prototype . update _button = function ( button _id , state _name ) {
if ( this . focused _editor . state _enabled ( state _name || button _id ) )
addElementClass ( button _id , "button_down" ) ;
else
removeElementClass ( button _id , "button_down" ) ;
}
Wiki . prototype . update _toolbar = function ( ) {
if ( this . focused _editor ) {
this . update _button ( "bold" ) ;
this . update _button ( "italic" ) ;
2007-08-27 21:01:42 +00:00
this . update _button ( "underline" ) ;
2007-07-16 20:22:38 +00:00
this . update _button ( "title" , "h3" ) ;
this . update _button ( "insertUnorderedList" ) ;
this . update _button ( "insertOrderedList" ) ;
this . update _button ( "createLink" ) ;
}
}
Wiki . prototype . toggle _link _button = function ( event ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
2007-08-30 20:17:10 +00:00
var link = null ;
2007-07-16 20:22:38 +00:00
2007-07-31 22:53:57 +00:00
if ( this . focused _editor && this . focused _editor . read _write ) {
2007-07-16 20:22:38 +00:00
this . focused _editor . focus ( ) ;
toggleElementClass ( "button_down" , "createLink" ) ;
if ( hasElementClass ( "createLink" , "button_down" ) )
this . focused _editor . start _link ( ) ;
else
2007-08-30 20:17:10 +00:00
link = this . focused _editor . end _link ( ) ;
2007-08-14 04:13:49 +00:00
2007-08-30 20:17:10 +00:00
this . display _link _pulldown ( this . focused _editor , link ) ;
2007-07-16 20:22:38 +00:00
}
event . stop ( ) ;
}
Wiki . prototype . hide _editor = function ( event , editor ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
if ( ! editor ) {
editor = this . focused _editor ;
this . focused _editor = null ;
}
if ( editor ) {
// before hiding an editor, save it
if ( this . read _write )
this . save _editor ( editor ) ;
editor . shutdown ( ) ;
2007-09-05 18:49:28 +00:00
this . display _empty _message ( ) ;
2007-07-16 20:22:38 +00:00
}
event . stop ( ) ;
}
Wiki . prototype . delete _editor = function ( event , editor ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
if ( ! editor ) {
editor = this . focused _editor ;
this . focused _editor = null ;
}
if ( editor ) {
2007-07-17 01:21:31 +00:00
if ( this . startup _notes [ editor . id ] )
delete this . startup _notes [ editor . id ] ;
2007-07-16 20:22:38 +00:00
2007-08-07 01:48:43 +00:00
this . save _editor ( editor , true ) ;
2007-09-20 22:26:43 +00:00
var self = this ;
2007-07-31 22:53:57 +00:00
if ( this . read _write && editor . read _write ) {
2007-07-17 01:21:31 +00:00
this . invoker . invoke ( "/notebooks/delete_note" , "POST" , {
2007-07-16 20:22:38 +00:00
"notebook_id" : this . notebook _id ,
2007-07-17 01:21:31 +00:00
"note_id" : editor . id
2007-09-20 22:26:43 +00:00
} , function ( result ) { self . display _storage _usage ( result . storage _bytes ) ; } ) ;
2007-07-16 20:22:38 +00:00
}
if ( editor == this . focused _editor )
this . focused _editor = null ;
2007-08-23 23:56:42 +00:00
if ( this . notebook . trash && ! editor . empty ( ) ) {
var undo _button = createDOM ( "input" , {
"type" : "button" ,
"class" : "message_button" ,
"value" : "undo" ,
"title" : "undo deletion"
} ) ;
2007-09-05 07:34:59 +00:00
var trash _link = createDOM ( "a" , {
2007-09-05 19:06:33 +00:00
"href" : "/notebooks/" + this . notebook . trash . object _id + "?parent_id=" + this . notebook . object _id
2007-09-05 07:34:59 +00:00
} , "trash" ) ;
this . display _message ( 'The note has been moved to the' , [ trash _link , ". " , undo _button ] )
2007-08-23 23:56:42 +00:00
var self = this ;
connect ( undo _button , "onclick" , function ( event ) { self . undelete _editor _via _undo ( event , editor ) ; } ) ;
}
2007-07-16 20:22:38 +00:00
2007-08-23 23:56:42 +00:00
editor . shutdown ( ) ;
2007-09-05 18:49:28 +00:00
this . display _empty _message ( ) ;
2007-08-07 01:48:43 +00:00
}
event . stop ( ) ;
}
Wiki . prototype . undelete _editor _via _trash = function ( event , editor ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
if ( ! editor ) {
editor = this . focused _editor ;
this . focused _editor = null ;
}
if ( editor ) {
if ( this . startup _notes [ editor . id ] )
delete this . startup _notes [ editor . id ] ;
this . save _editor ( editor , true ) ;
if ( this . read _write && editor . read _write ) {
2007-09-20 22:26:43 +00:00
var self = this ;
2007-08-07 01:48:43 +00:00
this . invoker . invoke ( "/notebooks/undelete_note" , "POST" , {
"notebook_id" : editor . deleted _from ,
"note_id" : editor . id
2007-09-20 22:26:43 +00:00
} , function ( result ) { self . display _storage _usage ( result . storage _bytes ) ; } ) ;
2007-08-07 01:48:43 +00:00
}
if ( editor == this . focused _editor )
this . focused _editor = null ;
editor . shutdown ( ) ;
2007-09-05 18:49:28 +00:00
this . display _empty _message ( ) ;
2007-08-07 01:48:43 +00:00
}
event . stop ( ) ;
}
Wiki . prototype . undelete _editor _via _undo = function ( event , editor ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
if ( editor ) {
if ( this . read _write && editor . read _write ) {
2007-09-20 22:26:43 +00:00
var self = this ;
2007-08-07 01:48:43 +00:00
this . invoker . invoke ( "/notebooks/undelete_note" , "POST" , {
"notebook_id" : this . notebook _id ,
"note_id" : editor . id
2007-09-20 22:26:43 +00:00
} , function ( result ) { self . display _storage _usage ( result . storage _bytes ) ; } ) ;
2007-08-07 01:48:43 +00:00
}
2007-08-10 19:19:47 +00:00
this . startup _notes [ editor . id ] = true ;
2007-08-24 20:50:07 +00:00
this . load _editor ( "Note not found." , editor . id , null ) ;
2007-08-07 01:48:43 +00:00
}
2007-07-16 20:22:38 +00:00
event . stop ( ) ;
}
2007-08-23 23:56:42 +00:00
Wiki . prototype . compare _versions = function ( event , editor , previous _revision ) {
this . clear _pulldowns ( ) ;
// display the two revisions for comparison by the user
2007-08-24 20:50:07 +00:00
this . load _editor ( editor . title , editor . id , previous _revision ) ;
this . load _editor ( editor . title , editor . id ) ;
2007-08-23 23:56:42 +00:00
}
2007-07-16 20:22:38 +00:00
Wiki . prototype . save _editor = function ( editor , fire _and _forget ) {
if ( ! editor )
editor = this . focused _editor ;
2007-07-26 01:18:41 +00:00
var self = this ;
2007-07-31 22:53:57 +00:00
if ( editor && editor . read _write && ! editor . empty ( ) ) {
2007-08-23 23:56:42 +00:00
var revisions = editor . revisions _list ;
2007-07-17 01:21:31 +00:00
this . invoker . invoke ( "/notebooks/save_note" , "POST" , {
2007-07-16 20:22:38 +00:00
"notebook_id" : this . notebook _id ,
2007-07-17 01:21:31 +00:00
"note_id" : editor . id ,
2007-07-16 20:22:38 +00:00
"contents" : editor . contents ( ) ,
2007-08-23 23:56:42 +00:00
"startup" : editor . startup ,
"previous_revision" : revisions . length ? revisions [ revisions . length - 1 ] : "None"
2007-09-20 22:26:43 +00:00
} , function ( result ) {
self . update _editor _revisions ( result , editor ) ;
self . display _storage _usage ( result . storage _bytes ) ;
} , null , fire _and _forget ) ;
2007-07-26 01:18:41 +00:00
}
}
Wiki . prototype . update _editor _revisions = function ( result , editor ) {
2007-08-23 23:56:42 +00:00
// if there's not a newly saved revision, then the contents are unchanged, so bail
if ( ! result . new _revision )
return ;
2007-07-26 01:18:41 +00:00
2007-08-23 23:56:42 +00:00
var revisions = editor . revisions _list ;
var client _previous _revision = revisions . length ? revisions [ revisions . length - 1 ] : null ;
// if the server's idea of the previous revision doesn't match the client's, then someone has
// gone behind our back and saved the editor's note from another window
if ( result . previous _revision != client _previous _revision ) {
var compare _button = createDOM ( "input" , {
"type" : "button" ,
"class" : "message_button" ,
"value" : "compare versions" ,
"title" : "compare your version with the modified version"
} ) ;
this . display _error ( 'Your changes to the note titled "' + editor . title + '" have overwritten changes made in another window.' , [ compare _button ] ) ;
var self = this ;
connect ( compare _button , "onclick" , function ( event ) {
self . compare _versions ( event , editor , result . previous _revision ) ;
} ) ;
revisions . push ( result . previous _revision ) ;
2007-07-16 20:22:38 +00:00
}
2007-08-23 23:56:42 +00:00
// add the new revision to the editor's revisions list
revisions . push ( result . new _revision ) ;
2007-07-16 20:22:38 +00:00
}
Wiki . prototype . search = function ( event ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
var self = this ;
2007-08-17 22:26:02 +00:00
this . invoker . invoke ( "/notebooks/search" , "GET" , {
"notebook_id" : this . notebook _id ,
"titles_only" : this . search _titles _only
} ,
2007-09-10 20:04:46 +00:00
function ( result ) { self . display _search _results ( result ) ; } ,
2007-07-16 20:22:38 +00:00
"search_form"
) ;
event . stop ( ) ;
}
2007-08-17 22:26:02 +00:00
Wiki . prototype . toggle _search _options = function ( event ) {
// if the pulldown is already open, then just close it
var existing _div = getElement ( "search_pulldown" ) ;
if ( existing _div ) {
existing _div . pulldown . shutdown ( ) ;
return ;
}
new Search _pulldown ( this , this . notebook _id , this . search _titles _only ) ;
event . stop ( ) ;
}
2007-09-10 20:04:46 +00:00
Wiki . prototype . display _search _results = function ( result ) {
2007-07-16 20:22:38 +00:00
// if there are no search results, indicate that and bail
2007-09-10 20:04:46 +00:00
if ( ! result || result . notes . length == 0 ) {
2007-08-07 01:48:43 +00:00
this . display _error ( "No matching notes." ) ;
2007-07-16 20:22:38 +00:00
return ;
}
2007-09-10 20:04:46 +00:00
// TODO: highlight the search term within the search results, idealy showing
// a section of the note contents including the search term
2007-07-23 23:42:53 +00:00
2007-09-10 20:04:46 +00:00
// if there's only one search result, automatically feel lucky^Wfortunate
if ( result . notes . length == 1 ) {
var note = result . notes [ 0 ]
// if the editor is already open, highlight it and bail
2007-07-23 23:42:53 +00:00
var iframe = getElement ( "note_" + note . object _id ) ;
if ( iframe ) {
2007-09-10 20:04:46 +00:00
iframe . editor . highlight ( ) ;
return ;
2007-07-23 23:42:53 +00:00
}
2007-09-10 20:04:46 +00:00
// otherwise, create an editor for the one note
this . create _editor ( note . object _id , note . contents , note . deleted _from , note . revisions _list , undefined , this . read _write , true , true ) ;
return ;
}
// otherwise, there are multiple search results, so create a "magic" search results note. but
// first close any open search results notes
if ( this . search _results _editor )
this . search _results _editor . shutdown ( ) ;
var list = createDOM ( "span" , { } ) ;
for ( var i in result . notes ) {
var note = result . notes [ i ]
if ( ! note . title ) continue ;
var contents _node = createDOM ( "span" , { } ) ;
contents _node . innerHTML = note . contents ;
contents = strip ( scrapeText ( contents _node ) ) ;
// remove the title from the scraped contents text
if ( contents . indexOf ( note . title ) == 0 )
contents = contents . substr ( note . title . length ) ;
if ( contents . length == 0 ) {
var preview = "empty note" ;
} else {
var max _preview _length = 160 ;
var preview = contents . substr ( 0 , max _preview _length ) + ( ( contents . length > max _preview _length ) ? "..." : "" ) ;
}
appendChildNodes ( list ,
createDOM ( "p" , { } ,
createDOM ( "a" , { "href" : "/notebooks/" + this . notebook _id + "?note_id=" + note . object _id } , note . title ) ,
createDOM ( "br" ) ,
createDOM ( "span" , { } , preview )
)
) ;
2007-07-16 20:22:38 +00:00
}
2007-09-10 20:04:46 +00:00
this . search _results _editor = this . create _editor ( "search_results" , "<h3>search results</h3>" + list . innerHTML , undefined , undefined , undefined , false , true , true ) ;
2007-07-16 20:22:38 +00:00
}
2007-09-04 21:37:48 +00:00
Wiki . prototype . display _all _notes _list = function ( result ) {
if ( result . notes . length == 0 ) {
2007-09-05 07:37:04 +00:00
this . display _message ( "This notebook is empty." ) ;
2007-09-04 21:37:48 +00:00
return ;
}
2007-09-11 19:19:03 +00:00
if ( this . all _notes _editor )
this . all _notes _editor . shutdown ( ) ;
2007-09-04 21:37:48 +00:00
// build up a list of all notes in this notebook, one link per note
2007-09-11 19:19:03 +00:00
var list = createDOM ( "ul" , { } ) ;
2007-09-04 21:37:48 +00:00
for ( var i in result . notes ) {
var note _tuple = result . notes [ i ]
var note _id = note _tuple [ 0 ] ;
var note _title = note _tuple [ 1 ] ;
appendChildNodes ( list ,
createDOM ( "li" , { } ,
createDOM ( "a" , { "href" : "/notebooks/" + this . notebook _id + "?note_id=" + note _id } , note _title )
)
) ;
}
2007-09-11 19:19:03 +00:00
this . all _notes _editor = this . create _editor ( "all_notes" , "<h3>all notes</h3>" + list . innerHTML , undefined , undefined , undefined , false , true , true ) ;
2007-09-04 21:37:48 +00:00
}
2007-09-05 07:34:59 +00:00
Wiki . prototype . display _message = function ( text , nodes ) {
2007-07-16 20:22:38 +00:00
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
2007-08-07 01:48:43 +00:00
var inner _div = DIV ( { "class" : "message_inner" } , text + " " ) ;
2007-09-05 07:34:59 +00:00
for ( var i in nodes )
appendChildNodes ( inner _div , nodes [ i ] ) ;
2007-08-07 01:48:43 +00:00
2007-09-20 23:25:59 +00:00
ok _button = createDOM ( "input" , {
"type" : "button" ,
"class" : "message_button" ,
"value" : "ok" ,
2007-09-20 23:34:34 +00:00
"title" : "dismiss this message"
2007-09-20 23:25:59 +00:00
} ) ;
appendChildNodes ( inner _div , ok _button ) ;
connect ( ok _button , "onclick" , this . clear _messages ) ;
2007-07-16 20:22:38 +00:00
var div = DIV ( { "class" : "message" } , inner _div ) ;
2007-09-05 07:34:59 +00:00
div . nodes = nodes ;
2007-08-07 01:48:43 +00:00
appendChildNodes ( "notes" , div ) ;
ScrollTo ( div ) ;
}
2007-09-05 07:34:59 +00:00
Wiki . prototype . display _error = function ( text , nodes ) {
2007-08-07 01:48:43 +00:00
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
2007-08-21 21:25:59 +00:00
// remove all empty editors, some of which might exist due to a problem reaching the server
var iframes = getElementsByTagAndClassName ( "iframe" , "note_frame" ) ;
for ( var i in iframes ) {
var editor = iframes [ i ] . editor ;
if ( editor . empty ( ) )
editor . shutdown ( ) ;
}
2007-08-23 23:56:42 +00:00
var inner _div = DIV ( { "class" : "error_inner" } , text + " " ) ;
2007-09-05 07:34:59 +00:00
for ( var i in nodes )
appendChildNodes ( inner _div , nodes [ i ] ) ;
2007-08-23 23:56:42 +00:00
2007-09-20 23:25:59 +00:00
ok _button = createDOM ( "input" , {
"type" : "button" ,
"class" : "message_button" ,
"value" : "ok" ,
2007-09-20 23:34:34 +00:00
"title" : "dismiss this message"
2007-09-20 23:25:59 +00:00
} ) ;
appendChildNodes ( inner _div , ok _button ) ;
connect ( ok _button , "onclick" , this . clear _messages ) ;
2007-08-07 01:48:43 +00:00
var div = DIV ( { "class" : "error" } , inner _div ) ;
2007-09-05 07:34:59 +00:00
div . nodes = nodes ;
2007-08-23 23:56:42 +00:00
2007-07-17 01:21:31 +00:00
appendChildNodes ( "notes" , div ) ;
2007-07-16 20:22:38 +00:00
ScrollTo ( div ) ;
}
Wiki . prototype . clear _messages = function ( ) {
var results = getElementsByTagAndClassName ( "div" , "message" ) ;
2007-08-07 01:48:43 +00:00
for ( var i in results ) {
var result = results [ i ] ;
blindUp ( result , options = { "duration" : 0.5 , afterFinish : function ( ) {
try {
2007-09-05 07:34:59 +00:00
for ( var j in result . nodes )
disconnectAll ( result . nodes [ j ] ) ;
2007-08-07 01:48:43 +00:00
removeElement ( result ) ;
} catch ( e ) { }
} } ) ;
}
var results = getElementsByTagAndClassName ( "div" , "error" ) ;
2007-07-16 20:22:38 +00:00
for ( var i in results ) {
var result = results [ i ] ;
blindUp ( result , options = { "duration" : 0.5 , afterFinish : function ( ) {
try {
removeElement ( result ) ;
} catch ( e ) { }
} } ) ;
}
}
Wiki . prototype . clear _pulldowns = function ( ) {
var results = getElementsByTagAndClassName ( "div" , "pulldown" ) ;
for ( var i in results ) {
var result = results [ i ] ;
2007-08-30 20:17:10 +00:00
// close the pulldown if it's been open at least a quarter second
if ( new Date ( ) - result . pulldown . init _time >= 250 )
result . pulldown . shutdown ( ) ;
2007-07-16 20:22:38 +00:00
}
}
2007-09-01 21:06:37 +00:00
Wiki . prototype . delete _all _editors = function ( event ) {
this . clear _messages ( ) ;
this . clear _pulldowns ( ) ;
this . startup _notes = new Array ( ) ;
if ( this . read _write ) {
2007-09-20 22:26:43 +00:00
var self = this ;
2007-09-01 21:06:37 +00:00
this . invoker . invoke ( "/notebooks/delete_all_notes" , "POST" , {
"notebook_id" : this . notebook _id
2007-09-20 22:26:43 +00:00
} , function ( result ) { self . display _storage _usage ( result . storage _bytes ) ; } ) ;
2007-09-01 21:06:37 +00:00
}
this . focused _editor = null ;
var iframes = getElementsByTagAndClassName ( "iframe" , "note_frame" ) ;
for ( var i in iframes ) {
var editor = iframes [ i ] . editor ;
editor . shutdown ( ) ;
}
2007-09-05 18:49:28 +00:00
this . display _empty _message ( ) ;
2007-09-01 21:06:37 +00:00
event . stop ( ) ;
}
2007-09-05 18:49:28 +00:00
Wiki . prototype . display _empty _message = function ( ) {
var iframes = getElementsByTagAndClassName ( "iframe" , "note_frame" ) ;
// if there are any open editors, bail
for ( var i in iframes ) {
var iframe = iframes [ i ] ;
if ( iframe . editor . closed == false )
return ;
}
if ( this . parent _id )
this . display _message ( "The trash is empty." )
}
2007-07-31 22:53:57 +00:00
Wiki . prototype . brief _revision = function ( revision ) {
return revision . split ( /\.\d/ ) [ 0 ] ; // strip off seconds from the timestamp
}
2007-07-26 01:18:41 +00:00
Wiki . prototype . toggle _editor _changes = function ( event , editor ) {
// if the pulldown is already open, then just close it
var pulldown _id = "changes_" + editor . id ;
var existing _div = getElement ( pulldown _id ) ;
if ( existing _div ) {
existing _div . pulldown . shutdown ( ) ;
return ;
}
2007-07-31 22:53:57 +00:00
new Changes _pulldown ( this , this . notebook _id , this . invoker , editor ) ;
2007-07-26 01:18:41 +00:00
event . stop ( ) ;
}
2007-07-16 20:22:38 +00:00
Wiki . prototype . toggle _editor _options = function ( event , editor ) {
// if the pulldown is already open, then just close it
2007-07-20 22:41:20 +00:00
var pulldown _id = "options_" + editor . id ;
var existing _div = getElement ( pulldown _id ) ;
2007-07-16 20:22:38 +00:00
if ( existing _div ) {
existing _div . pulldown . shutdown ( ) ;
return ;
}
2007-07-31 22:53:57 +00:00
new Options _pulldown ( this , this . notebook _id , this . invoker , editor ) ;
2007-07-20 22:41:20 +00:00
event . stop ( ) ;
}
2007-09-06 21:36:39 +00:00
connect ( window , "onload" , function ( event ) { new Wiki ( new Invoker ( ) ) ; } ) ;
2007-07-20 22:41:20 +00:00
2007-08-14 04:13:49 +00:00
function Pulldown ( wiki , notebook _id , pulldown _id , anchor , relative _to ) {
2007-07-31 22:53:57 +00:00
this . wiki = wiki ;
2007-07-16 20:22:38 +00:00
this . notebook _id = notebook _id ;
2007-07-20 22:41:20 +00:00
this . div = createDOM ( "div" , { "id" : pulldown _id , "class" : "pulldown" } ) ;
2007-07-16 20:22:38 +00:00
this . div . pulldown = this ;
2007-08-30 20:17:10 +00:00
this . init _time = new Date ( ) ;
2007-08-14 04:13:49 +00:00
this . anchor = anchor ;
this . relative _to = relative _to ;
2007-07-16 20:22:38 +00:00
addElementClass ( this . div , "invisible" ) ;
appendChildNodes ( document . body , this . div ) ;
2007-08-14 04:13:49 +00:00
var position = calculate _position ( anchor , relative _to ) ;
2007-07-16 20:22:38 +00:00
setElementPosition ( this . div , position ) ;
removeElementClass ( this . div , "invisible" ) ;
2007-08-14 04:13:49 +00:00
}
function calculate _position ( anchor , relative _to ) {
// position the pulldown under the anchor
var position = getElementPosition ( anchor ) ;
if ( relative _to ) {
var relative _pos = getElementPosition ( relative _to ) ;
if ( relative _pos ) {
position . x += relative _pos . x ;
position . y += relative _pos . y ;
2007-08-15 20:35:02 +00:00
// Work around an IE "feature" in which an element within an iframe changes its absolute
// position based on how far the page is scrolled. The if is necessary to prevent this
// workaround from screwing positions up in sane browsers like Firefox.
if ( getStyle ( "content" , "position" ) == "absolute" )
position . y -= getElement ( "html" ) . scrollTop ;
2007-08-14 04:13:49 +00:00
}
}
var anchor _dimensions = getElementDimensions ( anchor ) ;
2007-08-15 20:35:02 +00:00
// if the anchor has no height, move the position down a bit by an arbitrary amount
2007-08-14 04:13:49 +00:00
if ( anchor _dimensions . h == 0 )
position . y += 8 ;
else
position . y += anchor _dimensions . h + 4 ;
return position ;
}
Pulldown . prototype . update _position = function ( ) {
var position = calculate _position ( this . anchor , this . relative _to ) ;
setElementPosition ( this . div , position ) ;
}
2007-07-16 20:22:38 +00:00
2007-07-20 22:41:20 +00:00
Pulldown . prototype . shutdown = function ( ) {
removeElement ( this . div ) ;
}
2007-07-31 22:53:57 +00:00
function Options _pulldown ( wiki , notebook _id , invoker , editor ) {
Pulldown . call ( this , wiki , notebook _id , "options_" + editor . id , editor . options _button ) ;
2007-07-20 22:41:20 +00:00
this . invoker = invoker ;
this . editor = editor ;
2007-07-26 01:18:41 +00:00
this . startup _checkbox = createDOM ( "input" , { "type" : "checkbox" , "class" : "pulldown_checkbox" } ) ;
2007-08-20 20:02:42 +00:00
this . startup _toggle = createDOM ( "a" , { "href" : "" , "class" : "pulldown_link" , "title" : "Display this note whenever the notebook is loaded." } ,
2007-07-20 22:41:20 +00:00
"show on startup"
) ;
2007-07-26 01:18:41 +00:00
appendChildNodes ( this . div , this . startup _checkbox ) ;
2007-07-20 22:41:20 +00:00
appendChildNodes ( this . div , this . startup _toggle ) ;
this . startup _checkbox . checked = editor . startup ;
var self = this ;
2007-07-26 01:18:41 +00:00
connect ( this . startup _checkbox , "onclick" , function ( event ) { self . startup _clicked ( event ) ; } ) ;
2007-07-20 22:41:20 +00:00
connect ( this . startup _toggle , "onclick" , function ( event ) { self . startup _clicked ( event ) ; event . stop ( ) ; } ) ;
}
2007-08-20 19:51:02 +00:00
Options _pulldown . prototype = new function ( ) { this . prototype = Pulldown . prototype ; } ;
2007-07-20 22:41:20 +00:00
Options _pulldown . prototype . constructor = Options _pulldown ;
Options _pulldown . prototype . startup _clicked = function ( event ) {
2007-07-16 20:22:38 +00:00
if ( event . target ( ) != this . startup _checkbox )
this . startup _checkbox . checked = this . startup _checkbox . checked ? false : true ;
this . editor . startup = this . startup _checkbox . checked ;
2007-07-31 22:53:57 +00:00
// save this note along with its toggled startup state
this . wiki . save _editor ( this . editor ) ;
2007-07-16 20:22:38 +00:00
}
2007-07-20 22:41:20 +00:00
Options _pulldown . prototype . shutdown = function ( ) {
Pulldown . prototype . shutdown . call ( this ) ;
2007-07-31 22:53:57 +00:00
disconnectAll ( this . startup _checkbox ) ;
2007-07-16 20:22:38 +00:00
disconnectAll ( this . startup _toggle ) ;
}
2007-07-26 01:18:41 +00:00
2007-07-31 22:53:57 +00:00
function Changes _pulldown ( wiki , notebook _id , invoker , editor ) {
Pulldown . call ( this , wiki , notebook _id , "changes_" + editor . id , editor . changes _button ) ;
2007-07-26 01:18:41 +00:00
this . invoker = invoker ;
this . editor = editor ;
2007-07-31 22:53:57 +00:00
this . links = new Array ( ) ;
2007-07-26 01:18:41 +00:00
// display list of revision timestamps in reverse chronological order
2007-07-31 22:53:57 +00:00
if ( isUndefinedOrNull ( this . editor . revisions _list ) || this . editor . revisions _list . length == 0 ) {
2007-07-26 01:18:41 +00:00
appendChildNodes ( this . div , createDOM ( "span" , "This note has no previous changes." ) ) ;
return ;
}
var revisions _list = clone ( this . editor . revisions _list ) ;
revisions _list . reverse ( ) ;
2007-07-31 22:53:57 +00:00
var self = this ;
2007-07-26 01:18:41 +00:00
for ( var i = 0 ; i < revisions _list . length ; ++ i ) {
var revision = revisions _list [ i ] ;
2007-07-31 22:53:57 +00:00
var short _revision = this . wiki . brief _revision ( revision ) ;
2007-07-26 01:18:41 +00:00
var href = "/notebooks/" + this . notebook _id + "?" + queryString (
[ "note_id" , "revision" ] ,
[ this . editor . id , revision ]
) ;
2007-07-31 22:53:57 +00:00
var link = createDOM ( "a" , { "href" : href , "class" : "pulldown_link" } , short _revision ) ;
this . links . push ( link ) ;
link . revision = revision ;
connect ( link , "onclick" , function ( event ) { self . link _clicked ( event , self . editor . id ) ; } ) ;
appendChildNodes ( this . div , link ) ;
2007-07-26 01:18:41 +00:00
appendChildNodes ( this . div , createDOM ( "br" ) ) ;
}
}
2007-08-20 19:51:02 +00:00
Changes _pulldown . prototype = new function ( ) { this . prototype = Pulldown . prototype ; } ;
2007-07-26 01:18:41 +00:00
Changes _pulldown . prototype . constructor = Changes _pulldown ;
2007-07-31 22:53:57 +00:00
Changes _pulldown . prototype . link _clicked = function ( event , note _id ) {
var revision = event . target ( ) . revision ;
2007-08-24 20:50:07 +00:00
this . wiki . load _editor ( "Revision not found." , note _id , revision ) ;
2007-07-31 22:53:57 +00:00
event . stop ( ) ;
}
2007-08-14 04:13:49 +00:00
Changes _pulldown . prototype . shutdown = function ( ) {
2007-07-31 22:53:57 +00:00
Pulldown . prototype . shutdown . call ( this ) ;
for ( var i in this . links )
disconnectAll ( this . links [ i ] ) ;
}
2007-08-14 04:13:49 +00:00
function Link _pulldown ( wiki , notebook _id , invoker , editor , link ) {
2007-08-16 01:29:18 +00:00
link . pulldown = this ;
2007-08-14 04:13:49 +00:00
this . link = link ;
Pulldown . call ( this , wiki , notebook _id , "link_" + editor . id , link , editor . iframe ) ;
this . invoker = invoker ;
this . editor = editor ;
2007-08-16 22:27:58 +00:00
this . title _field = createDOM ( "input" , { "class" : "text_field" , "size" : "30" , "maxlength" : "256" } ) ;
2007-08-14 04:13:49 +00:00
this . note _preview = createDOM ( "span" , { } ) ;
this . previous _title = "" ;
var self = this ;
connect ( this . title _field , "onclick" , function ( event ) { self . title _field _clicked ( event ) ; } ) ;
2007-08-20 18:43:45 +00:00
connect ( this . title _field , "onfocus" , function ( event ) { self . title _field _focused ( event ) ; } ) ;
2007-08-14 04:13:49 +00:00
connect ( this . title _field , "onchange" , function ( event ) { self . title _field _changed ( event ) ; } ) ;
connect ( this . title _field , "onblur" , function ( event ) { self . title _field _changed ( event ) ; } ) ;
connect ( this . title _field , "onkeydown" , function ( event ) { self . title _field _key _pressed ( event ) ; } ) ;
appendChildNodes ( this . div , createDOM ( "span" , { "class" : "field_label" } , "links to: " ) ) ;
appendChildNodes ( this . div , this . title _field ) ;
appendChildNodes ( this . div , this . note _preview ) ;
2007-08-16 22:27:58 +00:00
// links with targets are considered links to external sites
if ( link . target ) {
2007-09-07 23:03:12 +00:00
this . title _field . value = link . href ;
replaceChildNodes ( this . note _preview , "web link" ) ;
2007-08-16 22:27:58 +00:00
return ;
}
2007-08-14 04:13:49 +00:00
var query = parse _query ( link ) ;
2007-08-15 00:18:30 +00:00
var title = link _title ( link , query ) ;
2007-08-14 04:13:49 +00:00
var id = query . note _id ;
2007-09-07 23:03:12 +00:00
// if the note has no destination note id set, try loading the note from the server by title
2007-08-16 22:27:58 +00:00
if ( ( id == undefined || id == "new" || id == "null" ) && title . length > 0 ) {
2007-09-07 23:03:12 +00:00
if ( title == "all notes" ) {
this . title _field . value = title ;
this . display _preview ( title , "list of all notes in this notebook" ) ;
return ;
}
2007-09-10 20:04:46 +00:00
if ( title == "search results" ) {
this . title _field . value = title ;
this . display _preview ( title , "current search results" ) ;
return ;
}
2007-08-16 20:11:01 +00:00
this . invoker . invoke (
"/notebooks/load_note_by_title" , "GET" , {
"notebook_id" : this . notebook _id ,
"note_title" : title
} ,
function ( result ) {
if ( result . note ) {
self . title _field . value = result . note . title ;
self . display _preview ( result . note . title , result . note . contents ) ;
} else {
self . title _field . value = title ;
replaceChildNodes ( self . note _preview , "empty note" ) ;
}
}
) ;
2007-08-14 04:13:49 +00:00
return ;
}
2007-08-14 20:34:31 +00:00
// if this link has an actual destination note id set, then see if that note is already open. if
// so, display its title and a preview of its contents
var iframe = getElement ( "note_" + id ) ;
if ( iframe ) {
2007-08-16 01:29:18 +00:00
this . title _field . value = iframe . editor . title ;
this . display _preview ( iframe . editor . title , iframe . editor . document ) ;
2007-08-14 20:34:31 +00:00
return ;
}
// otherwise, load the destination note from the server, displaying its title and a preview of
// its contents
2007-08-14 04:13:49 +00:00
this . invoker . invoke (
"/notebooks/load_note" , "GET" , {
"notebook_id" : this . notebook _id ,
"note_id" : id
} ,
function ( result ) {
if ( result . note ) {
self . title _field . value = result . note . title ;
self . display _preview ( result . note . title , result . note . contents ) ;
} else {
2007-08-15 00:18:30 +00:00
self . title _field . value = title ;
2007-08-14 04:13:49 +00:00
replaceChildNodes ( self . note _preview , "empty note" ) ;
}
}
) ;
}
2007-08-20 19:51:02 +00:00
Link _pulldown . prototype = new function ( ) { this . prototype = Pulldown . prototype ; } ;
2007-08-14 04:13:49 +00:00
Link _pulldown . prototype . constructor = Link _pulldown ;
Link _pulldown . prototype . display _preview = function ( title , contents ) {
2007-08-16 01:29:18 +00:00
if ( ! contents ) {
replaceChildNodes ( this . note _preview , "empty note" ) ;
return ;
}
2007-08-14 20:56:13 +00:00
// if contents is a DOM node, just scrape its text
if ( contents . nodeType ) {
contents = strip ( scrapeText ( contents ) ) ;
// otherwise, assume contents is a string, so put it into a DOM node and then scrape its contents
} else {
2007-08-14 20:34:31 +00:00
var contents _node = createDOM ( "span" , { } ) ;
contents _node . innerHTML = contents ;
2007-08-14 20:56:13 +00:00
contents = strip ( scrapeText ( contents _node ) ) ;
2007-08-14 20:34:31 +00:00
}
2007-08-14 04:13:49 +00:00
// remove the title from the scraped contents text
if ( contents . indexOf ( title ) == 0 )
contents = contents . substr ( title . length ) ;
if ( contents . length == 0 ) {
replaceChildNodes ( this . note _preview , "empty note" ) ;
} else {
var max _preview _length = 40 ;
var preview = contents . substr ( 0 , max _preview _length ) + ( ( contents . length > max _preview _length ) ? "..." : "" ) ;
replaceChildNodes ( this . note _preview , preview ) ;
}
}
Link _pulldown . prototype . title _field _clicked = function ( event ) {
event . stop ( ) ;
}
2007-08-20 18:43:45 +00:00
Link _pulldown . prototype . title _field _focused = function ( event ) {
this . title _field . select ( ) ;
}
2007-08-14 04:13:49 +00:00
Link _pulldown . prototype . title _field _changed = function ( event ) {
// if the title is actually unchanged, then bail
if ( this . title _field . value == this . previous _title )
return ;
replaceChildNodes ( this . note _preview , "" ) ;
2007-08-15 00:18:30 +00:00
var title = strip ( this . title _field . value ) ;
this . previous _title = title ;
2007-08-14 04:13:49 +00:00
2007-08-16 01:29:18 +00:00
var self = this ;
this . wiki . resolve _link ( title , this . link , function ( contents ) {
self . display _preview ( title , contents ) ;
} ) ;
2007-08-14 04:13:49 +00:00
}
Link _pulldown . prototype . title _field _key _pressed = function ( event ) {
// if enter is pressed, consider the title field altered. this is necessary because IE neglects
// to issue an onchange event when enter is pressed in an input field
if ( event . key ( ) . code == 13 ) {
this . title _field _changed ( ) ;
event . stop ( ) ;
}
}
Link _pulldown . prototype . update _position = function ( anchor , relative _to ) {
Pulldown . prototype . update _position . call ( this , anchor , relative _to ) ;
}
Link _pulldown . prototype . shutdown = function ( ) {
Pulldown . prototype . shutdown . call ( this ) ;
disconnectAll ( this . title _field ) ;
if ( this . link )
2007-08-16 01:29:18 +00:00
this . link . pulldown = null ;
2007-08-14 04:13:49 +00:00
}
2007-08-17 22:26:02 +00:00
function Search _pulldown ( wiki , notebook _id , titles _only ) {
Pulldown . call ( this , wiki , notebook _id , "search_pulldown" , getElement ( "search_button" ) ) ;
this . titles _radio = createDOM ( "input" , { "type" : "radio" , "class" : "pulldown_radio" } ) ;
2007-08-20 20:02:42 +00:00
this . titles _toggle = createDOM ( "a" , { "href" : "" , "class" : "pulldown_link" , "title" : "Search only note titles." } ,
2007-08-17 22:26:02 +00:00
"titles only"
) ;
this . everything _radio = createDOM ( "input" , { "type" : "radio" , "class" : "pulldown_radio" } ) ;
2007-08-20 20:02:42 +00:00
this . everything _toggle = createDOM ( "a" , { "href" : "" , "class" : "pulldown_link" , "title" : "Search everything, including note titles and contents." } ,
2007-08-17 22:26:02 +00:00
"everything"
) ;
appendChildNodes ( this . div , this . titles _radio ) ;
appendChildNodes ( this . div , this . titles _toggle ) ;
appendChildNodes ( this . div , createDOM ( "br" ) ) ;
appendChildNodes ( this . div , this . everything _radio ) ;
appendChildNodes ( this . div , this . everything _toggle ) ;
if ( titles _only )
this . titles _radio . checked = true ;
else
this . everything _radio . checked = true ;
var self = this ;
connect ( this . titles _radio , "onclick" , function ( event ) { self . titles _clicked ( event ) ; } ) ;
connect ( this . titles _toggle , "onclick" , function ( event ) { self . titles _clicked ( event ) ; } ) ;
connect ( this . everything _radio , "onclick" , function ( event ) { self . everything _clicked ( event ) ; } ) ;
connect ( this . everything _toggle , "onclick" , function ( event ) { self . everything _clicked ( event ) ; } ) ;
}
2007-08-20 19:51:02 +00:00
Search _pulldown . prototype = new function ( ) { this . prototype = Pulldown . prototype ; } ;
2007-08-17 22:26:02 +00:00
Search _pulldown . prototype . constructor = Search _pulldown ;
Search _pulldown . prototype . titles _clicked = function ( event ) {
if ( event . target ( ) != this . titles _radio )
this . titles _radio . checked = true ;
this . everything _radio . checked = false ;
this . wiki . search _titles _only = true ;
event . stop ( ) ;
}
Search _pulldown . prototype . everything _clicked = function ( event ) {
if ( event . target ( ) != this . everything _radio )
this . everything _radio . checked = true ;
this . titles _radio . checked = false ;
this . wiki . search _titles _only = false ;
event . stop ( ) ;
}
Search _pulldown . prototype . shutdown = function ( ) {
Pulldown . prototype . shutdown . call ( this ) ;
disconnectAll ( this . titles _radio ) ;
disconnectAll ( this . titles _toggle ) ;
disconnectAll ( this . everything _radio ) ;
disconnectAll ( this . everything _toggle ) ;
}