Refactored the way note summaries are created in the link pulldown window.
This commit is contained in:
parent
45f94aa188
commit
0152b49475
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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 );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue