diff --git a/controller/Notebooks.py b/controller/Notebooks.py
index 97e31c4..a07a62a 100644
--- a/controller/Notebooks.py
+++ b/controller/Notebooks.py
@@ -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
diff --git a/controller/test/Test_notebooks.py b/controller/test/Test_notebooks.py
index 568daa1..d7e3985 100644
--- a/controller/test/Test_notebooks.py
+++ b/controller/test/Test_notebooks.py
@@ -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"
the title
" + 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"the title
" + 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"the title
" + 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()
diff --git a/model/Note.py b/model/Note.py
index 26789ca..bf76576 100644
--- a/model/Note.py
+++ b/model/Note.py
@@ -10,7 +10,7 @@ class Note( Persistent ):
TITLE_PATTERN = re.compile( u"(.*?)
", 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 )
diff --git a/model/Notebook.py b/model/Notebook.py
index 9a95afe..86cae87 100644
--- a/model/Notebook.py
+++ b/model/Notebook.py
@@ -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
diff --git a/model/test/Test_note.py b/model/test/Test_note.py
index 2226084..d0920de 100644
--- a/model/test/Test_note.py
+++ b/model/test/Test_note.py
@@ -8,18 +8,20 @@ class Test_note( object ):
self.object_id = u"17"
self.title = u"title goes here"
self.contents = u"%s
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
diff --git a/static/js/Editor.js b/static/js/Editor.js
index a6b3d1c..2d29719 100644
--- a/static/js/Editor.js
+++ b/static/js/Editor.js
@@ -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 )
diff --git a/static/js/Wiki.js b/static/js/Wiki.js
index 773454e..7f42481 100644
--- a/static/js/Wiki.js
+++ b/static/js/Wiki.js
@@ -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 + " ...";
+ // if the summary appears not to end with a complete sentence, add "..."
+ if ( !/[?!.]\s*$/.test( summary ) )
+ summary = summary + " ...";
}
- 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 );
} );
}