function Wiki( invoker ) { this.next_id = null; this.focused_editor = null; this.blank_editor_id = null; this.notebook = null; this.notebook_id = getElement( "notebook_id" ).value; this.parent_id = getElement( "parent_id" ).value; // id of the notebook containing this one this.read_write = false; 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 = null; this.storage_usage_high = false; connect( this.invoker, "error_message", this, "display_error" ); connect( this.invoker, "message", this, "display_message" ); connect( "search_form", "onsubmit", this, "search" ); connect( "html", "onclick", this, "background_clicked" ); // get info on the requested notebook (if any) var self = this; if ( this.notebook_id ) { this.invoker.invoke( "/notebooks/contents", "GET", { "notebook_id": this.notebook_id, "note_id": getElement( "note_id" ).value, "revision": getElement( "revision" ).value }, function( result ) { self.populate( result ); } ); var include_startup_notes = false; } else { var include_startup_notes = true; } // get info on the current user (logged-in or anonymous) this.invoker.invoke( "/users/current", "GET", { "include_startup_notes": include_startup_notes }, 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; this.populate( { "notebook" : result.notebooks[ 0 ], "startup_notes": result.startup_notes } ); } 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" ) ); return; } // display links for current notebook and a list of all notebooks that the user has access to var notebooks_span = createDOM( "span" ); replaceChildNodes( "notebooks_area", notebooks_span ); appendChildNodes( notebooks_span, createDOM( "h4", "notebooks" ) ); for ( var i in result.notebooks ) { var notebook = result.notebooks[ i ]; if ( notebook.name == "Luminotes" || notebook.name == "trash" ) continue; var div_class = "link_area_item"; if ( notebook.object_id == this.notebook_id ) div_class += " current_notebook_name"; appendChildNodes( notebooks_span, createDOM( "div", { "class": div_class }, createDOM( "a", { "href": "/notebooks/" + notebook.object_id, "id": "notebook_" + notebook.object_id }, notebook.name ) ) ); } this.rate_plan = result.rate_plan; this.display_storage_usage( result.user.storage_bytes ); // display the name of the logged in user and a logout link appendChildNodes( user_span, "logged in as " + ( result.user.username || "a guest" ) ); appendChildNodes( user_span, " | " ); appendChildNodes( user_span, createDOM( "a", { "href": result.http_url + "/", "id": "logout_link" }, "logout" ) ); var self = this; connect( "logout_link", "onclick", function ( event ) { self.save_editor( null, true ); self.invoker.invoke( "/users/logout", "POST" ); event.stop(); } ); } Wiki.prototype.display_storage_usage = function( storage_bytes ) { if ( !storage_bytes ) return; // 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 ); if ( usage_percent > 90 ) { var storage_usage_class = "storage_usage_high"; 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." ); this.storage_usage_high = true; } else if ( usage_percent > 75 ) { var storage_usage_class = "storage_usage_medium"; this.storage_usage_high = false; } else { var storage_usage_class = "storage_usage_low"; this.storage_usage_high = false; } 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" ) ); } Wiki.prototype.populate = function ( result ) { this.notebook = result.notebook; var self = this; var header_area = getElement( "notebook_header_area" ); replaceChildNodes( header_area, createDOM( "b", {}, this.notebook.name ) ); if ( this.parent_id ) { 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); } } ); appendChildNodes( header_area, createDOM( "span", {}, " | " ) ); appendChildNodes( header_area, createDOM( "a", { "href": "/notebooks/" + this.parent_id }, "return to notebook" ) ); } var span = createDOM( "span" ); replaceChildNodes( "this_notebook_area", span ); appendChildNodes( span, createDOM( "h4", "this notebook" ) ); 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" ) ) ); } if ( this.notebook.name != "Luminotes" ) { 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" ) ) ); } if ( this.notebook.read_write ) { this.read_write = true; removeElementClass( "toolbar", "undisplayed" ); if ( this.notebook.trash_id ) { appendChildNodes( span, createDOM( "div", { "class": "link_area_item" }, createDOM( "a", { "href": "/notebooks/" + this.notebook.trash_id + "?parent_id=" + this.notebook.object_id, "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", { "href": location.href, "id": "trash_link", "title": "Look here for notes you've deleted." }, "trash" ) ) ); 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" ); } 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" ); } ); connect( "underline", "onclick", function ( event ) { self.toggle_button( event, "underline" ); } ); 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" ); connect( "newNote", "onmousedown", function ( event ) { addElementClass( "newNote", "button_down" ); } ); connect( "newNote", "onmouseup", this, "create_blank_editor" ); // grab the next available object id this.invoker.invoke( "/next_id", "POST", null, function( result ) { self.update_next_id( result ); } ); } var self = this; if ( !this.parent_id ) { connect( "all_notes_link", "onclick", function ( event ) { self.load_editor( "all notes", "null" ); event.stop(); } ); } if ( this.notebook.name != "Luminotes" ) { connect( "download_html_link", "onclick", function ( event ) { self.save_editor( null, true ); } ); } // create an editor for each startup note in the received notebook, focusing the first one var focus = true; for ( var i in result.startup_notes ) { var note = result.startup_notes[ i ]; if ( !note ) continue; this.startup_notes[ note.object_id ] = true; // don't actually create an editor if a particular note was provided in the result if ( !result.note ) { var editor = this.create_editor( note.object_id, note.contents, note.deleted_from_id, note.revision, this.read_write, false, focus ); this.open_editors[ note.title ] = editor; focus = false; } } // if one particular note was provided, then just display an editor for that note var read_write = this.read_write; var revision_element = getElement( "revision" ); if ( revision_element && revision_element.value ) read_write = false; if ( result.note ) this.create_editor( result.note.object_id, result.note.contents || getElement( "note_contents" ).value, result.note.deleted_from_id, result.note.revision, read_write, false, true ); if ( result.startup_notes.length == 0 && !result.note ) this.display_empty_message(); } Wiki.prototype.background_clicked = function ( event ) { if ( !hasElementClass( event.target(), "pulldown_checkbox" ) ) this.clear_pulldowns(); } Wiki.prototype.create_blank_editor = function ( event ) { if ( event ) event.stop(); removeElementClass( "newNote", "button_down" ); // 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; } // if there is already a blank editor, then highlight it and bail if ( this.blank_editor_id != null ) { var blank_iframe_id = "note_" + this.blank_editor_id; var iframe = getElement( blank_iframe_id ); if ( iframe && iframe.editor.empty() ) { iframe.editor.highlight(); return; } } var editor = this.create_editor( undefined, undefined, undefined, undefined, this.read_write, true, true ); this.blank_editor_id = editor.id; } Wiki.prototype.load_editor = function ( note_title, note_id, revision, link ) { if ( this.notebook.name == "trash" && !revision ) { this.display_message( "If you'd like to use this note, try undeleting it first." ); return; } // if a link is given with an open link pulldown, then ignore the note title given and use the // one from the pulldown instead 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; } // 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" ); } // 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; } } // if there's not a valid destination note id, then load by title instead of by id 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 ) { editor.highlight(); return; } this.display_search_results(); return; } // 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; } } this.invoker.invoke( "/notebooks/load_note_by_title", "GET", { "notebook_id": this.notebook_id, "note_title": note_title, "revision": revision }, function ( result ) { self.parse_loaded_editor( result, note_title, revision, link ); } ); return; } this.invoker.invoke( "/notebooks/load_note", "GET", { "notebook_id": this.notebook_id, "note_id": note_id, "revision": revision }, function ( result ) { self.parse_loaded_editor( result, note_title, revision, link ); } ); } Wiki.prototype.resolve_link = function ( note_title, link, callback ) { // 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" ); return; } link.removeAttribute( "target" ); if ( note_title == "all notes" || note_title == "search results" ) { 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" ); else callback( "current search results" ); } return; } var id = parse_query( link ).note_id; // if the link already has a valid-looking id, it's already resolved, so bail if ( !callback && id != undefined && id != "new" && id != "null" ) return; if ( note_title.length == 0 ) return; // 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; } var self = this; this.invoker.invoke( "/notebooks/" + ( callback ? "load_note_by_title" : "lookup_note_id" ), "GET", { "notebook_id": this.notebook_id, "note_title": note_title }, function ( result ) { 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 ); } ); } Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_revision, link ) { if ( result.note ) { var id = result.note.object_id; if ( requested_revision ) id += " " + requested_revision; var actual_revision = result.note.revision; var note_text = result.note.contents; var deleted_from_id = result.note.deleted; } else { var id = null; var note_text = "
Previous revision from " + short_revision + "
" + note_text; } var startup = this.startup_notes[ id ]; var editor = new Editor( id, this.notebook_id, note_text, deleted_from_id, revision, read_write, startup, highlight, focus ); if ( this.read_write ) { connect( editor, "state_changed", this, "editor_state_changed" ); connect( editor, "title_changed", this, "editor_title_changed" ); connect( editor, "key_pressed", this, "editor_key_pressed" ); connect( editor, "delete_clicked", function ( event ) { self.delete_editor( event, editor ) } ); connect( editor, "undelete_clicked", function ( event ) { self.undelete_editor_via_trash( event, editor ) } ); connect( editor, "changes_clicked", function ( event ) { self.toggle_editor_changes( event, editor ) } ); 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" ); connect( editor, "resolve_link", this, "resolve_link" ); 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 ); } ); return editor; } Wiki.prototype.editor_state_changed = function ( editor ) { this.update_toolbar(); this.display_link_pulldown( editor ); } 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; } Wiki.prototype.display_link_pulldown = function ( editor, link ) { if ( !editor.read_write ) { this.clear_pulldowns(); return; } if ( !link ) link = editor.find_link_at_cursor(); // 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 ) { this.clear_pulldowns(); return; } var pulldown = link.pulldown; if ( pulldown ) pulldown.update_position(); // if the cursor is now on a link, display a link pulldown if there isn't already one open if ( link_title( link ).length > 0 ) { if ( !pulldown ) { this.clear_pulldowns(); new Link_pulldown( this, this.notebook_id, this.invoker, editor, link ); } } } Wiki.prototype.editor_focused = function ( editor, fire_and_forget ) { if ( editor ) addElementClass( editor.iframe, "focused_note_frame" ); if ( this.focused_editor && this.focused_editor != editor ) { this.clear_pulldowns(); removeElementClass( this.focused_editor.iframe, "focused_note_frame" ); // 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(); this.display_empty_message(); } 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 ) { alert( editor.contents() ); 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" ); // ctrl-u: underline } else if ( code == 85 ) { this.toggle_button( event, "underline" ); // ctrl-t: title } else if ( code == 84 ) { this.toggle_button( event, "title", "h3" ); // ctrl-period: unordered list } else if ( code == 190 ) { this.toggle_button( event, "insertUnorderedList" ); // ctrl-n: ordered list } else if ( code == 49 ) { this.toggle_button( event, "insertOrderedList" ); // ctrl-l: make a note link } else if ( code == 76 ) { this.toggle_link_button( event ); // ctrl-n: new note } else if ( code == 78 ) { this.create_blank_editor( event ); // ctrl-h: hide note } else if ( code == 72 ) { if ( !editor.deleted_from_id ) this.hide_editor( event ); // ctrl-d: delete note } 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(); if ( this.focused_editor && this.focused_editor.read_write ) { 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" ); this.update_button( "underline" ); 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(); var link = null; if ( this.focused_editor && this.focused_editor.read_write ) { this.focused_editor.focus(); toggleElementClass( "button_down", "createLink" ); if ( hasElementClass( "createLink", "button_down" ) ) this.focused_editor.start_link(); else link = this.focused_editor.end_link(); this.display_link_pulldown( this.focused_editor, link ); } 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(); this.display_empty_message(); } 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 ) { if ( this.startup_notes[ editor.id ] ) delete this.startup_notes[ editor.id ]; this.save_editor( editor, true ); var self = this; if ( this.read_write && editor.read_write ) { this.invoker.invoke( "/notebooks/delete_note", "POST", { "notebook_id": this.notebook_id, "note_id": editor.id }, function ( result ) { self.display_storage_usage( result.storage_bytes ); } ); } if ( editor == this.focused_editor ) this.focused_editor = null; if ( this.notebook.trash_id && !editor.empty() ) { var undo_button = createDOM( "input", { "type": "button", "class": "message_button", "value": "undo", "title": "undo deletion" } ); var trash_link = createDOM( "a", { "href": "/notebooks/" + this.notebook.trash_id + "?parent_id=" + this.notebook.object_id }, "trash" ); this.display_message( 'The note has been moved to the', [ trash_link, ". ", undo_button ] ) var self = this; connect( undo_button, "onclick", function ( event ) { self.undelete_editor_via_undo( event, editor ); } ); } editor.shutdown(); this.display_empty_message(); } 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 ) { var self = this; this.invoker.invoke( "/notebooks/undelete_note", "POST", { "notebook_id": editor.deleted_from_id, "note_id": editor.id }, function ( result ) { self.display_storage_usage( result.storage_bytes ); } ); } if ( editor == this.focused_editor ) this.focused_editor = null; editor.shutdown(); this.display_empty_message(); } 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 ) { var self = this; this.invoker.invoke( "/notebooks/undelete_note", "POST", { "notebook_id": this.notebook_id, "note_id": editor.id }, function ( result ) { self.display_storage_usage( result.storage_bytes ); } ); } this.startup_notes[ editor.id ] = true; this.load_editor( "Note not found.", editor.id, null ); } event.stop(); } Wiki.prototype.compare_versions = function( event, editor, previous_revision ) { this.clear_pulldowns(); // display the two revisions for comparison by the user this.load_editor( editor.title, editor.id, previous_revision ); this.load_editor( editor.title, editor.id ); } Wiki.prototype.save_editor = function ( editor, fire_and_forget ) { if ( !editor ) editor = this.focused_editor; var self = this; if ( editor && editor.read_write && !editor.empty() ) { this.invoker.invoke( "/notebooks/save_note", "POST", { "notebook_id": this.notebook_id, "note_id": editor.id, "contents": editor.contents(), "startup": editor.startup, "previous_revision": editor.revision ? editor.revision : "None" }, function ( result ) { self.update_editor_revisions( result, editor ); self.display_storage_usage( result.storage_bytes ); }, null, fire_and_forget ); } } Wiki.prototype.update_editor_revisions = function ( result, editor ) { // if there's not a newly saved revision, then the contents are unchanged, so bail if ( !result.new_revision ) return; var client_previous_revision = editor.revision; editor.revision = result.new_revision; // 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 ); } ); if ( !editor.revisions_list || editor.revisions_list.length == 0 ) return; editor.revisions_list.push( result.previous_revision ); } // add the new revision to the editor's revisions list if ( !editor.revisions_list || editor.revisions_list.length == 0 ) return; editor.revisions_list.push( result.new_revision ); } Wiki.prototype.search = function ( event ) { this.clear_messages(); this.clear_pulldowns(); var self = this; this.invoker.invoke( "/notebooks/search", "GET", { "notebook_id": this.notebook_id }, function( result ) { self.display_search_results( result ); }, "search_form" ); event.stop(); } Wiki.prototype.display_search_results = function ( result ) { // if there are no search results, indicate that and bail if ( !result || result.notes.length == 0 ) { this.display_error( "No matching notes." ); return; } // TODO: highlight the search term within the search results, idealy showing // a section of the note contents including the search term // 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 var iframe = getElement( "note_" + note.object_id ); if ( iframe ) { iframe.editor.highlight(); return; } // otherwise, create an editor for the one note this.create_editor( note.object_id, note.contents, note.deleted_from_id, note.revision, 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 ) ) ); } this.search_results_editor = this.create_editor( "search_results", "