witten
/
luminotes
Archived
1
0
Fork 0

Refactored the way note summaries are created in the link pulldown window.

This commit is contained in:
Dan Helfman 2007-11-27 21:28:25 +00:00
parent 45f94aa188
commit 0152b49475
7 changed files with 298 additions and 78 deletions

View File

@ -1,3 +1,4 @@
import re
import cherrypy
from datetime import datetime
from Expose import expose
@ -29,6 +30,8 @@ class Access_error( Exception ):
class Notebooks( object ):
WHITESPACE_PATTERN = re.compile( u"\s+" )
"""
Controller for dealing with notebooks and their notes, corresponding to the "/notebooks" URL.
"""
@ -161,9 +164,10 @@ class Notebooks( object ):
notebook_id = Valid_id(),
note_id = Valid_id(),
revision = Valid_revision(),
summarize = Valid_bool(),
user_id = Valid_id( none_okay = True ),
)
def load_note( self, notebook_id, note_id, revision = None, user_id = None ):
def load_note( self, notebook_id, note_id, revision = None, summarize = False, user_id = None ):
"""
Return the information on a particular note by its id.
@ -173,6 +177,9 @@ class Notebooks( object ):
@param note_id: id of note to return
@type revision: unicode or NoneType
@param revision: revision timestamp of the note (optional)
@type summarize: bool or NoneType
@param summarize: True to return a summary of the note's contents, False to return full text
(optional, defaults to False)
@type user_id: unicode or NoneType
@param user_id: id of current logged-in user (if any), determined by @grab_user_id
@rtype: json dict
@ -196,7 +203,7 @@ class Notebooks( object ):
if notebook and note.notebook_id == notebook.trash_id:
if revision:
return dict(
note = note,
note = summarize and self.summarize_note( note ) or note,
)
return dict(
@ -207,7 +214,7 @@ class Notebooks( object ):
raise Access_error()
return dict(
note = note,
note = summarize and self.summarize_note( note ) or note,
)
@expose( view = Json )
@ -216,9 +223,10 @@ class Notebooks( object ):
@validate(
notebook_id = Valid_id(),
note_title = Valid_string( min = 1, max = 500 ),
summarize = Valid_bool(),
user_id = Valid_id( none_okay = True ),
)
def load_note_by_title( self, notebook_id, note_title, user_id ):
def load_note_by_title( self, notebook_id, note_title, summarize = False, user_id = None ):
"""
Return the information on a particular note by its title.
@ -226,6 +234,9 @@ class Notebooks( object ):
@param notebook_id: id of notebook the note is in
@type note_title: unicode
@param note_title: title of the note to return
@type summarize: bool or NoneType
@param summarize: True to return a summary of the note's contents, False to return full text
(optional, defaults to False)
@type user_id: unicode or NoneType
@param user_id: id of current logged-in user (if any), determined by @grab_user_id
@rtype: json dict
@ -244,9 +255,59 @@ class Notebooks( object ):
note = self.__database.select_one( Note, notebook.sql_load_note_by_title( note_title ) )
return dict(
note = note,
note = summarize and self.summarize_note( note ) or note,
)
def summarize_note( self, note ):
"""
Create a truncated note summary for the given note, and then return the note with its summary
set.
@type note: model.Note or NoneType
@param note: note to summarize, or None
@rtype: model.Note or NoneType
@return: note with its summary member set, or None if no note was provided
"""
MAX_SUMMARY_LENGTH = 40
word_count = 10
if note is None:
return None
if note.contents is None:
return note
# remove all HTML from the contents and also remove the title
summary = Html_nuker().nuke( note.contents ).strip()
if note.title and summary.startswith( note.title ):
summary = summary[ len( note.title ) : ]
# split the summary on whitespace
words = self.WHITESPACE_PATTERN.split( summary )
def first_words( words, word_count ):
return u" ".join( words[ : word_count ] )
# find a summary less than MAX_SUMMARY_LENGTH and, if possible, truncated on a word boundary
truncated = False
summary = first_words( words, word_count )
while len( summary ) > MAX_SUMMARY_LENGTH:
word_count -= 1
summary = first_words( words, word_count )
# if the first word is just ridiculously long, truncate it without finding a word boundary
if word_count == 1:
summary = summary[ : MAX_SUMMARY_LENGTH ]
truncated = True
break
if truncated or word_count < len( words ):
summary += " ..."
note.summary = summary
return note
@expose( view = Json )
@strongly_expire
@grab_user_id
@ -607,8 +668,8 @@ class Notebooks( object ):
"""
Search the notes within a particular notebook for the given search text. Note that the search
is case-insensitive, and all HTML tags are ignored. Notes with title matches are generally
ranked higher than matches that are only in the note contents. The returned notes have their
normal contents replaced with summary contents with the search terms highlighted.
ranked higher than matches that are only in the note contents. The returned notes include
content summaries with the search terms highlighted.
@type notebook_id: unicode
@param notebook_id: id of notebook to search

View File

@ -384,6 +384,24 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0
def test_load_note_with_summary( self ):
self.login()
result = self.http_post( "/notebooks/load_note/", dict(
notebook_id = self.notebook.object_id,
note_id = self.note.object_id,
summarize = True,
), session_id = self.session_id )
note = result[ "note" ]
assert note.object_id == self.note.object_id
assert note.title == self.note.title
assert note.contents == self.note.contents
assert note.summary == u"blah"
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0
def test_load_note_by_title( self ):
self.login()
@ -435,6 +453,64 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0
def test_load_note_by_title_with_summary( self ):
self.login()
result = self.http_post( "/notebooks/load_note_by_title/", dict(
notebook_id = self.notebook.object_id,
note_title = self.note.title,
summarize = True,
), session_id = self.session_id )
note = result[ "note" ]
assert note.object_id == self.note.object_id
assert note.title == self.note.title
assert note.contents == self.note.contents
assert note.summary == u"blah"
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0
def test_summarize_note( self ):
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == u"blah"
def test_summarize_note_truncated_at_word_boundary( self ):
self.note.contents = u"<h3>the title</h3>" + u"foo bar baz quux " * 10
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == u"foo bar baz quux foo bar baz quux foo ..."
def test_summarize_note_truncated_at_character_boundary( self ):
self.note.contents = u"<h3>the title</h3>" + u"foobarbazquux" * 10
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == u"foobarbazquuxfoobarbazquuxfoobarbazquuxf ..."
def test_summarize_note_with_short_words( self ):
self.note.contents = u"<h3>the title</h3>" + u"a b c d e f g h i j k l"
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == u"a b c d e f g h i j ..."
def test_summarize_note_without_title( self ):
self.note.contents = "foo bar baz quux"
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == u"foo bar baz quux"
def test_summarize_note_without_contents( self ):
self.note.contents = None
note = cherrypy.root.notebooks.summarize_note( self.note )
assert note.summary == None
def test_summarize_note_none( self ):
note = cherrypy.root.notebooks.summarize_note( None )
assert note == None
def test_lookup_note_id( self ):
self.login()

View File

@ -10,7 +10,7 @@ class Note( Persistent ):
TITLE_PATTERN = re.compile( u"<h3>(.*?)</h3>", flags = re.IGNORECASE )
def __init__( self, object_id, revision = None, title = None, contents = None, notebook_id = None,
startup = None, deleted_from_id = None, rank = None, creation = None ):
startup = None, deleted_from_id = None, rank = None, creation = None, summary = None ):
"""
Create a new note with the given id and contents.
@ -32,12 +32,15 @@ class Note( Persistent ):
@param rank: indicates numeric ordering of this note in relation to other startup notes
@type creation: datetime or NoneType
@param creation: creation timestamp of the object (optional, defaults to None)
@type summary: unicode or NoneType
@param summary: textual summary of the note's contents (optional, defaults to None)
@rtype: Note
@return: newly constructed note
"""
Persistent.__init__( self, object_id, revision )
self.__title = title
self.__contents = contents
self.__summary = summary
self.__notebook_id = notebook_id
self.__startup = startup or False
self.__deleted_from_id = deleted_from_id
@ -45,7 +48,7 @@ class Note( Persistent ):
self.__creation = creation
@staticmethod
def create( object_id, contents = None, notebook_id = None, startup = None, rank = None, creation = None ):
def create( object_id, contents = None, notebook_id = None, startup = None, rank = None, creation = None, summary = None ):
"""
Convenience constructor for creating a new note.
@ -61,10 +64,12 @@ class Note( Persistent ):
@param rank: indicates numeric ordering of this note in relation to other startup notes
@type creation: datetime or NoneType
@param creation: creation timestamp of the object (optional, defaults to None)
@type summary: unicode or NoneType
@param summary: textual summary of the note's contents (optional, defaults to None)
@rtype: Note
@return: newly constructed note
"""
note = Note( object_id, notebook_id = notebook_id, startup = startup, rank = rank, creation = creation )
note = Note( object_id, notebook_id = notebook_id, startup = startup, rank = rank, creation = creation, summary = summary )
note.contents = contents
return note
@ -86,6 +91,9 @@ class Note( Persistent ):
else:
self.__title = None
def __set_summary( self, summary ):
self.__summary = summary
def __set_notebook_id( self, notebook_id ):
self.__notebook_id = notebook_id
self.update_revision()
@ -141,6 +149,7 @@ class Note( Persistent ):
d = Persistent.to_dict( self )
d.update( dict(
contents = self.__contents,
summary = self.__summary,
title = self.__title,
deleted_from_id = self.__deleted_from_id,
creation = self.__creation,
@ -150,6 +159,7 @@ class Note( Persistent ):
title = property( lambda self: self.__title )
contents = property( lambda self: self.__contents, __set_contents )
summary = property( lambda self: self.__summary, __set_summary )
notebook_id = property( lambda self: self.__notebook_id, __set_notebook_id )
startup = property( lambda self: self.__startup, __set_startup )
deleted_from_id = property( lambda self: self.__deleted_from_id, __set_deleted_from_id )

View File

@ -161,9 +161,10 @@ class Notebook( Persistent ):
return \
"""
select id, revision, title, headline( drop_html_tags( contents ), query ), notebook_id, startup, deleted_from_id from (
select id, revision, title, contents, notebook_id, startup, deleted_from_id, rank, null,
headline( drop_html_tags( contents ), query ) as summary from (
select
id, revision, title, contents, notebook_id, startup, deleted_from_id, query, rank_cd( search, query ) as rank
id, revision, title, contents, notebook_id, startup, deleted_from_id, rank_cd( search, query ) as rank, null, query
from
note_current, to_tsquery( 'default', %s ) query
where

View File

@ -8,18 +8,20 @@ class Test_note( object ):
self.object_id = u"17"
self.title = u"title goes here"
self.contents = u"<h3>%s</h3>blah" % self.title
self.summary = None
self.notebook_id = u"18"
self.startup = False
self.rank = 17.5
self.creation = datetime.now()
self.delta = timedelta( seconds = 1 )
self.note = Note.create( self.object_id, self.contents, self.notebook_id, self.startup, self.rank, self.creation )
self.note = Note.create( self.object_id, self.contents, self.notebook_id, self.startup, self.rank, self.creation, self.summary )
def test_create( self ):
assert self.note.object_id == self.object_id
assert datetime.now( tz = utc ) - self.note.revision < self.delta
assert self.note.contents == self.contents
assert self.note.summary == None
assert self.note.title == self.title
assert self.note.notebook_id == self.notebook_id
assert self.note.startup == self.startup
@ -36,6 +38,7 @@ class Test_note( object ):
assert self.note.revision > previous_revision
assert self.note.contents == new_contents
assert self.note.summary == None
assert self.note.title == new_title
assert self.note.notebook_id == self.notebook_id
assert self.note.startup == self.startup
@ -53,6 +56,7 @@ class Test_note( object ):
# html should be stripped out of the title
assert self.note.revision > previous_revision
assert self.note.contents == new_contents
assert self.note.summary == None
assert self.note.title == new_title
assert self.note.notebook_id == self.notebook_id
assert self.note.startup == self.startup
@ -70,6 +74,7 @@ class Test_note( object ):
# should only use the first title
assert self.note.revision > previous_revision
assert self.note.contents == new_contents
assert self.note.summary == None
assert self.note.title == new_title
assert self.note.notebook_id == self.notebook_id
assert self.note.startup == self.startup
@ -77,6 +82,22 @@ class Test_note( object ):
assert self.note.rank == self.rank
assert self.note.creation == self.creation
def test_set_summary( self ):
summary = u"summary goes here..."
original_revision = self.note.revision
self.note.summary = summary
assert self.note.revision == original_revision
assert self.note.contents == self.contents
assert self.note.summary == summary
assert self.note.title == self.title
assert self.note.notebook_id == self.notebook_id
assert self.note.startup == self.startup
assert self.note.deleted_from_id == None
assert self.note.rank == self.rank
assert self.note.creation == self.creation
def test_set_notebook_id( self ):
previous_revision = self.note.revision
self.note.notebook_id = u"54"
@ -111,6 +132,7 @@ class Test_note( object ):
assert d.get( "object_id" ) == self.note.object_id
assert datetime.now( tz = utc ) - d.get( "revision" ) < self.delta
assert d.get( "contents" ) == self.contents
assert d.get( "summary" ) == self.summary
assert d.get( "title" ) == self.title
assert d.get( "deleted_from_id" ) == None
assert d.get( "creation" ) == self.note.creation
@ -121,6 +143,7 @@ class Test_note_blank( Test_note ):
self.object_id = u"17"
self.title = None
self.contents = None
self.summary = None
self.notebook_id = None
self.startup = False
self.rank = None
@ -133,6 +156,7 @@ class Test_note_blank( Test_note ):
assert self.note.object_id == self.object_id
assert datetime.now( tz = utc ) - self.note.revision < self.delta
assert self.note.contents == None
assert self.note.summary == None
assert self.note.title == None
assert self.note.notebook_id == None
assert self.note.startup == False

View File

@ -383,8 +383,7 @@ Editor.prototype.start_link = function () {
} else {
this.link_started = null;
this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
var link = this.find_link_at_cursor();
signal( this, "resolve_link", link_title( link ), link );
return this.find_link_at_cursor();
}
} else if ( this.document.selection ) { // browsers such as IE
var range = this.document.selection.createRange();
@ -400,8 +399,7 @@ Editor.prototype.start_link = function () {
} else {
this.link_started = null;
this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
var link = this.find_link_at_cursor();
signal( this, "resolve_link", link_title( link ), link );
return this.find_link_at_cursor();
}
}
}
@ -430,7 +428,6 @@ Editor.prototype.end_link = function () {
range.pasteHTML( "" );
}
signal( this, "resolve_link", link_title( link ), link );
return link;
}
@ -562,6 +559,48 @@ Editor.prototype.shutdown = function( event ) {
} } );
}
Editor.prototype.summarize = function () {
var summary = strip( scrapeText( this.document.body ) );
// remove the title from the scraped summary text
if ( summary.indexOf( this.title ) == 0 )
summary = summary.substr( this.title.length );
if ( summary.length == 0 )
return null;
var MAX_SUMMARY_LENGTH = 40;
var word_count = 10;
// split the summary on whitespace
var words = summary.split( /\s+/ );
function first_words( words, word_count ) {
return words.slice( 0, word_count ).join( " " );
}
var truncated = false;
summary = first_words( words, word_count );
// find a summary less than MAX_SUMMARY_LENGTH and, if possible, truncated on a word boundary
while ( summary.length > MAX_SUMMARY_LENGTH ) {
word_count -= 1;
summary = first_words( words, word_count );
// if the first word is just ridiculously long, truncate it without finding a word boundary
if ( word_count == 1 ) {
summary = summary.substr( 0, MAX_SUMMARY_LENGTH );
truncated = true;
break;
}
}
if ( truncated || word_count < words.length )
summary += " ...";
return summary;
}
// convenience function for parsing a link that has an href URL containing a query string
function parse_query( link ) {
if ( !link || !link.href )

View File

@ -490,22 +490,43 @@ Wiki.prototype.resolve_link = function ( note_title, link, callback ) {
}
var self = this;
if ( callback ) {
this.invoker.invoke(
"/notebooks/load_note_by_title", "GET", {
"notebook_id": this.notebook_id,
"note_title": note_title,
"summarize": true
},
function ( result ) {
if ( result && result.note ) {
link.href = "/notebooks/" + self.notebook_id + "?note_id=" + result.note.object_id;
} else {
link.href = "/notebooks/" + self.notebook_id + "?" + queryString(
[ "title", "note_id" ],
[ note_title, "null" ]
);
}
callback( ( result && result.note ) ? result.note.summary : null );
}
);
return;
}
this.invoker.invoke(
"/notebooks/" + ( callback ? "load_note_by_title" : "lookup_note_id" ), "GET", {
"/notebooks/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 );
if ( result && result.note_id ) {
link.href = "/notebooks/" + self.notebook_id + "?note_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 );
}
);
}
@ -596,7 +617,6 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
}
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 );
@ -857,11 +877,18 @@ Wiki.prototype.toggle_link_button = function ( event ) {
if ( this.focused_editor && this.focused_editor.read_write ) {
this.focused_editor.focus();
if ( this.toggle_image_button( "createLink" ) )
this.focused_editor.start_link();
link = this.focused_editor.start_link();
else
link = this.focused_editor.end_link();
this.display_link_pulldown( this.focused_editor, link );
if ( link ) {
var self = this;
this.resolve_link( link_title( link ), link, function ( summary ) {
self.display_link_pulldown( self.focused_editor, link );
} );
} else {
this.display_link_pulldown( this.focused_editor );
}
}
event.stop();
@ -1124,23 +1151,23 @@ Wiki.prototype.display_search_results = function ( result ) {
if ( !note.title ) continue;
if ( note.contents.length == 0 ) {
var preview = "empty note";
var summary = "empty note";
} else {
var preview = note.contents;
var summary = note.summary;
// if the preview appears not to end with a complete sentence, add "..."
if ( !/[?!.]\s*$/.test( preview ) )
preview = preview + " <b>...</b>";
// if the summary appears not to end with a complete sentence, add "..."
if ( !/[?!.]\s*$/.test( summary ) )
summary = summary + " <b>...</b>";
}
var preview_span = createDOM( "span" );
preview_span.innerHTML = preview;
var summary_span = createDOM( "span" );
summary_span.innerHTML = summary;
appendChildNodes( list,
createDOM( "p", {},
createDOM( "a", { "href": "/notebooks/" + this.notebook_id + "?note_id=" + note.object_id }, note.title ),
createDOM( "br" ),
preview_span
summary_span
)
);
}
@ -1756,7 +1783,7 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
this.invoker = invoker;
this.editor = editor;
this.title_field = createDOM( "input", { "class": "text_field", "size": "30", "maxlength": "256" } );
this.note_preview = createDOM( "span", {} );
this.note_summary = createDOM( "span", {} );
this.previous_title = "";
var self = this;
@ -1768,12 +1795,12 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
appendChildNodes( this.div, createDOM( "span", { "class": "field_label" }, "links to: " ) );
appendChildNodes( this.div, this.title_field );
appendChildNodes( this.div, this.note_preview );
appendChildNodes( this.div, this.note_summary );
// links with targets are considered links to external sites
if ( link.target ) {
this.title_field.value = link.href;
replaceChildNodes( this.note_preview, "web link" );
replaceChildNodes( this.note_summary, "web link" );
return;
}
@ -1785,20 +1812,21 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
if ( ( id == undefined || id == "new" || id == "null" ) && title.length > 0 ) {
if ( title == "all notes" ) {
this.title_field.value = title;
this.display_preview( title, "list of all notes in this notebook" );
this.display_summary( title, "list of all notes in this notebook" );
return;
}
if ( title == "search results" ) {
this.title_field.value = title;
this.display_preview( title, "current search results" );
this.display_summary( title, "current search results" );
return;
}
this.invoker.invoke(
"/notebooks/load_note_by_title", "GET", {
"notebook_id": this.notebook_id,
"note_title": title
"note_title": title,
"summarize": true
},
function ( result ) {
// if the user has already started typing something, don't overwrite it
@ -1806,10 +1834,10 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
return;
if ( result.note ) {
self.title_field.value = result.note.title;
self.display_preview( result.note.title, result.note.contents );
self.display_summary( result.note.title, result.note.summary );
} else {
self.title_field.value = title;
replaceChildNodes( self.note_preview, "empty note" );
replaceChildNodes( self.note_summary, "empty note" );
}
}
);
@ -1817,20 +1845,21 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
}
// 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
// so, display its title and a summary of its contents
var iframe = getElement( "note_" + id );
if ( iframe ) {
this.title_field.value = iframe.editor.title;
this.display_preview( iframe.editor.title, iframe.editor.document );
this.display_summary( iframe.editor.title, iframe.editor.summarize() );
return;
}
// otherwise, load the destination note from the server, displaying its title and a preview of
// otherwise, load the destination note from the server, displaying its title and a summary of
// its contents
this.invoker.invoke(
"/notebooks/load_note", "GET", {
"notebook_id": this.notebook_id,
"note_id": id
"note_id": id,
"summarize": true
},
function ( result ) {
// if the user has already started typing something, don't overwrite it
@ -1838,10 +1867,10 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
return;
if ( result.note ) {
self.title_field.value = result.note.title;
self.display_preview( result.note.title, result.note.contents );
self.display_summary( result.note.title, result.note.summary );
} else {
self.title_field.value = title;
replaceChildNodes( self.note_preview, "empty note" );
replaceChildNodes( self.note_summary, "empty note" );
}
}
);
@ -1850,33 +1879,13 @@ function Link_pulldown( wiki, notebook_id, invoker, editor, link ) {
Link_pulldown.prototype = new function () { this.prototype = Pulldown.prototype; };
Link_pulldown.prototype.constructor = Link_pulldown;
Link_pulldown.prototype.display_preview = function ( title, contents ) {
if ( !contents ) {
replaceChildNodes( this.note_preview, "empty note" );
return;
}
// 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 {
var contents_node = createDOM( "span", {} );
contents_node.innerHTML = contents;
contents = strip( scrapeText( contents_node ) );
}
// 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.display_summary = function ( title, summary ) {
if ( !summary )
replaceChildNodes( this.note_summary, "empty note" );
else if ( summary.length == 0 )
replaceChildNodes( this.note_summary, "empty note" );
else
replaceChildNodes( this.note_summary, summary );
}
Link_pulldown.prototype.title_field_clicked = function ( event ) {
@ -1892,13 +1901,13 @@ Link_pulldown.prototype.title_field_changed = function ( event ) {
if ( this.title_field.value == this.previous_title )
return;
replaceChildNodes( this.note_preview, "" );
replaceChildNodes( this.note_summary, "" );
var title = strip( this.title_field.value );
this.previous_title = title;
var self = this;
this.wiki.resolve_link( title, this.link, function ( contents ) {
self.display_preview( title, contents );
this.wiki.resolve_link( title, this.link, function ( summary ) {
self.display_summary( title, summary );
} );
}