2008-10-03 06:08:56 +00:00
|
|
|
GECKO = /Gecko/.test( navigator.userAgent ) && !/like Gecko/.test( navigator.userAgent );
|
|
|
|
WEBKIT = /WebKit/.test( navigator.userAgent );
|
2009-01-14 00:12:34 +00:00
|
|
|
MSIE6 = /MSIE 6\.0/.test( navigator.userAgent );
|
|
|
|
MSIE = /MSIE/.test( navigator.userAgent );
|
2009-01-10 00:43:33 +00:00
|
|
|
OPERA = /Opera/.test( navigator.userAgent );
|
2008-10-03 06:08:56 +00:00
|
|
|
|
2009-01-22 20:08:20 +00:00
|
|
|
FRAME_BORDER_WIDTH = 2;
|
|
|
|
FRAME_BORDER_HEIGHT = 2;
|
|
|
|
|
|
|
|
|
|
|
|
function Shared_iframe() {
|
|
|
|
this.iframe = createDOM( "iframe",
|
|
|
|
{
|
|
|
|
// iframe src attribute is necessary in IE 6 on an HTTPS site to prevent annoying warnings
|
|
|
|
"src": MSIE6 && "/static/html/blank.html" || "about:blank",
|
|
|
|
"frameBorder": "0",
|
|
|
|
"scrolling": "no",
|
2009-01-23 22:49:03 +00:00
|
|
|
"class": "note_frame invisible",
|
|
|
|
"height": "0"
|
2009-01-22 20:08:20 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
insertSiblingNodesAfter( "iframe_area", this.iframe );
|
|
|
|
|
|
|
|
// enable design mode
|
|
|
|
if ( this.iframe.contentDocument ) { // browsers such as Firefox
|
|
|
|
this.iframe.contentDocument.designMode = "On";
|
|
|
|
} else { // browsers such as IE
|
|
|
|
this.iframe.contentWindow.document.designMode = "On";
|
2009-01-21 21:33:11 +00:00
|
|
|
}
|
2009-01-24 22:21:07 +00:00
|
|
|
|
|
|
|
if ( this.iframe.contentDocument ) { // browsers such as Firefox
|
|
|
|
var document = this.iframe.contentDocument;
|
|
|
|
} else { // browsers such as IE
|
|
|
|
var document = this.iframe.contentWindow.document;
|
|
|
|
}
|
|
|
|
|
|
|
|
document.open();
|
|
|
|
var padding = '1.5em';
|
|
|
|
if ( GECKO )
|
|
|
|
padding = '1.5em 1.5em 1em 1.5em';
|
|
|
|
else if ( MSIE )
|
|
|
|
padding = '1.5em 1.5em 2.25em 1.5em';
|
|
|
|
else if ( WEBKIT )
|
|
|
|
padding = '0.4em 1.5em 1em 1.5em';
|
|
|
|
|
|
|
|
document.write(
|
2009-03-19 05:19:42 +00:00
|
|
|
'<html><head><style>html { padding: 0em; margin: 0; } body { padding: ' + padding + '; margin: 0; font-size: 90%; line-height: 140%; font-family: sans-serif; white-space: -moz-pre-wrap; white-space: pre-wrap; word-wrap: break-word; } h3 { padding-bottom: 0.25em; border-bottom: 1px solid #dddddd; margin-bottom: 0.75em; } a[target ^= "_new"] { background: url(/static/images/web_icon_tiny.png) right center no-repeat; padding-right: 13px; } .diff a[target ^= "_new"] { background-image: none; padding-right: 0; } a:hover { color: #ff6600; } img { border-width: 0; } .left_justified { float: left; margin: 0.5em 1.5em 0.5em 0; } .center_justified { display: block; margin: 0.5em auto 0.5em auto; text-align: center; } .right_justified { float: right; margin: 0.5em 0 0.5em 1.5em; } hr { border: 0; color: #000000; background-color: #000000; height: 1px; } ul { list-style-type: disc; } ul li { margin-top: 0.5em; } ol li { margin-top: 0.5em; } .center_text { text-align: center; } .small_text { padding-top: 0.5em; font-size: 90%; } .indented { margin-left: 1em; } .thumbnail_left { float: left; margin: 0.5em; margin-right: 1em; margin-bottom: 0.5em; border: 1px solid #999999; } .thumbnail_right { float: right; margin: 0.5em; margin-left: 1em; margin-bottom: 0.5em; border: 1px solid #999999; }</style>' +
|
2009-01-24 22:21:07 +00:00
|
|
|
'<meta content="text/html; charset=UTF-8" http-equiv="content-type"></meta></head><body></body></html>'
|
|
|
|
);
|
|
|
|
document.close();
|
2009-01-22 20:08:20 +00:00
|
|
|
}
|
|
|
|
|
2009-01-25 07:48:10 +00:00
|
|
|
Shared_iframe.prototype.editor = function () {
|
|
|
|
return this.iframe.editor;
|
|
|
|
}
|
|
|
|
|
2008-10-03 06:08:56 +00:00
|
|
|
|
2008-10-27 23:23:27 +00:00
|
|
|
function Editor( id, notebook_id, note_text, deleted_from_id, revision, read_write, startup, highlight, focus, position_after, start_dirty, own_notes_only ) {
|
2007-07-16 20:22:38 +00:00
|
|
|
this.id = id;
|
2007-08-11 21:34:56 +00:00
|
|
|
this.notebook_id = notebook_id;
|
2007-07-26 01:18:41 +00:00
|
|
|
this.initial_text = note_text;
|
2008-05-18 08:07:15 +00:00
|
|
|
this.start_dirty = start_dirty;
|
Merged revisions 401-446 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/branches/postgres
................
r402 | witten | 2007-10-04 00:48:49 -0700 (Thu, 04 Oct 2007) | 3 lines
Initialized merge tracking via "svnmerge" with revisions "1-401" from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
................
r404 | witten | 2007-10-04 01:17:07 -0700 (Thu, 04 Oct 2007) | 2 lines
Beginning a conversion from bsddb to postgres.
................
r405 | witten | 2007-10-04 01:18:58 -0700 (Thu, 04 Oct 2007) | 9 lines
Merged revisions 402-404 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
........
r403 | witten | 2007-10-04 01:14:45 -0700 (Thu, 04 Oct 2007) | 2 lines
Yay, no more stupid deprecation warnings from simplejson about the sre module.
........
................
r406 | witten | 2007-10-04 15:34:39 -0700 (Thu, 04 Oct 2007) | 4 lines
* Switched back to Python 2.4 because many Python modules in Debian are not packaged to work with Python 2.5
* Began removal of all references to Scheduler, @async, yield, and so on.
* Converted Database.py to support PostgreSQL and updated its unit tests accordingly.
................
r407 | witten | 2007-10-04 16:34:01 -0700 (Thu, 04 Oct 2007) | 2 lines
All unit tests for the new model classes now pass.
................
r409 | witten | 2007-10-05 00:53:56 -0700 (Fri, 05 Oct 2007) | 2 lines
Reordering some columns and adding some indices.
................
r410 | witten | 2007-10-05 16:08:37 -0700 (Fri, 05 Oct 2007) | 4 lines
Now adding trash notebooks to user_notebook table. Also switching db
conversion/verification tools back to require Python 2.5, since they still use
the old Scheduler, which requires 2.5 generator features.
................
r411 | witten | 2007-10-06 16:26:56 -0700 (Sat, 06 Oct 2007) | 2 lines
Lots more unit tests passing. Most of the recent work was on controller.Users and related stuff.
................
r412 | witten | 2007-10-07 01:52:12 -0700 (Sun, 07 Oct 2007) | 2 lines
controller.Users unit tests now finally pass!
................
r413 | witten | 2007-10-07 02:14:10 -0700 (Sun, 07 Oct 2007) | 3 lines
Got controller.Root unit tests passing.
Moved fake sql_* function shenanigans from Test_users.py to Test_controller.py, for use by other controller unit tests.
................
r414 | witten | 2007-10-08 23:11:11 -0700 (Mon, 08 Oct 2007) | 2 lines
All unit tests pass! Fuck yeah!
................
r415 | witten | 2007-10-08 23:13:07 -0700 (Mon, 08 Oct 2007) | 2 lines
Removing all references to Scheduler from luminotes.py
................
r416 | witten | 2007-10-08 23:54:51 -0700 (Mon, 08 Oct 2007) | 3 lines
Converted deleted_from to deleted_from_id in a few more places.
Fixed bug in Users.contents().
................
r417 | witten | 2007-10-09 00:11:59 -0700 (Tue, 09 Oct 2007) | 3 lines
Typo fix in Note sql method.
Adding autocommit flag to Database.next_id() method.
................
r418 | witten | 2007-10-09 00:13:19 -0700 (Tue, 09 Oct 2007) | 2 lines
Updating unit test for new auto commit flag.
................
r419 | witten | 2007-10-09 00:14:09 -0700 (Tue, 09 Oct 2007) | 2 lines
Removing debugging print.
................
r420 | witten | 2007-10-09 00:20:55 -0700 (Tue, 09 Oct 2007) | 2 lines
More sql fixes. I really need some funtional tests that hit the database and exercise the SQL.
................
r421 | witten | 2007-10-09 00:51:34 -0700 (Tue, 09 Oct 2007) | 3 lines
Fixed controller.Database handling of tuple as an Object_type.
Made SQL for user storage calculation better at handling null values and also more succinct.
................
r422 | witten | 2007-10-09 13:32:16 -0700 (Tue, 09 Oct 2007) | 2 lines
Converting Wiki.js to trash_id notebook member instead of trash object.
................
r423 | witten | 2007-10-09 13:42:10 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer displaying "download as html" on the front page, as people see "download" and think they're downloading the software.
................
r424 | witten | 2007-10-09 14:24:40 -0700 (Tue, 09 Oct 2007) | 2 lines
Notebooks.contents() now returns notebooks with correct read-write status.
................
r425 | witten | 2007-10-09 14:32:25 -0700 (Tue, 09 Oct 2007) | 2 lines
Fixed reporting of validation errors to the user. Now says "The blah is missing." instead of just "is missing"
................
r426 | witten | 2007-10-09 17:05:22 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer redirecting to trash notebook upon login.
................
r427 | witten | 2007-10-09 17:20:33 -0700 (Tue, 09 Oct 2007) | 2 lines
Made controller.Database use a connection pool.
................
r429 | witten | 2007-10-09 20:13:30 -0700 (Tue, 09 Oct 2007) | 2 lines
Converted initdb.py and updatedb.py to Postgres from bsddb.
................
r430 | witten | 2007-10-09 20:37:14 -0700 (Tue, 09 Oct 2007) | 2 lines
Changing error message to remove underscores from variable names.
................
r431 | witten | 2007-10-10 13:23:30 -0700 (Wed, 10 Oct 2007) | 2 lines
Removing unused note_title parameter from Wiki.create_editor().
................
r432 | witten | 2007-10-10 13:25:16 -0700 (Wed, 10 Oct 2007) | 2 lines
Revision regular expression now supports timezone notation.
................
r433 | witten | 2007-10-10 14:43:47 -0700 (Wed, 10 Oct 2007) | 2 lines
Finished implementing ranked ordering for startup notes. (However, there's no way to change the rank from the client yet.)
................
r434 | witten | 2007-10-10 16:25:19 -0700 (Wed, 10 Oct 2007) | 4 lines
More strict access checking. Fixed oversight in Postgres DB conversion where,
in certain controller.Notebook methods, access was only checked at the
notebook level, not at the note level as well.
................
r435 | witten | 2007-10-10 17:45:18 -0700 (Wed, 10 Oct 2007) | 3 lines
Now loading revisions on demand from client when the "changes" button is clicked. Also caching
loading revisions so subsequent clicks don't have to reload.
................
r436 | witten | 2007-10-10 21:31:20 -0700 (Wed, 10 Oct 2007) | 2 lines
Tweaking some of the error handling in Expose and Root so that unhandled errors give a generic error message to the client.
................
r437 | witten | 2007-10-10 21:33:49 -0700 (Wed, 10 Oct 2007) | 2 lines
The release script no longer runs initdb.py, because the default database is no longer a single file included in the tarball.
................
r438 | witten | 2007-10-10 21:40:11 -0700 (Wed, 10 Oct 2007) | 2 lines
Updated install instructuctions to include use of initdb.py.
................
r439 | witten | 2007-10-10 21:56:42 -0700 (Wed, 10 Oct 2007) | 3 lines
Made initdb.py only nuke (drop tables/views) when given a command-line flag.
Also made install directions more correct.
................
r440 | witten | 2007-10-10 21:58:48 -0700 (Wed, 10 Oct 2007) | 2 lines
IE 6 doesn't like commas.
................
r441 | witten | 2007-10-10 22:08:50 -0700 (Wed, 10 Oct 2007) | 4 lines
load your notebook. without clicking on "changes", edit a note that has previous revisions. click on "changes". it'll only show
the most recent revision. fixed by not appending to changes as a result of a save unless the client-side revisions list cache has
something in it
................
r442 | witten | 2007-10-10 23:30:41 -0700 (Wed, 10 Oct 2007) | 2 lines
Forgot to actually save off the new revision as editor.revision.
................
r443 | witten | 2007-10-11 01:35:54 -0700 (Thu, 11 Oct 2007) | 13 lines
More intelligent datetime handling:
* convertdb.py assumes old bsddb database timestamps are Pacific, and then
converts them to UTC before inserting them into the new PostgreSQL
database.
* No longer using naked timezoneless datetime objects in model/controller
code, except in unit tests that need compatability with pysqlite. Now
using UTC everwhere.
* Asking PostgreSQL to give us all timestamps back in UTC.
* New dependency on python-tz (pytz) package, noted in INSTALL doc.
* Client now responsible for converting UTC timestamps to local time for
display.
................
r444 | witten | 2007-10-11 01:46:09 -0700 (Thu, 11 Oct 2007) | 2 lines
Tweak to prevent potential race in IE.
................
r445 | witten | 2007-10-11 01:49:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Got JavaScript "unit" tests passing again.
................
r446 | witten | 2007-10-11 01:53:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Noting that js tests require the Luminotes server on localhost.
................
2007-10-11 09:03:43 +00:00
|
|
|
this.deleted_from_id = deleted_from_id || null;
|
|
|
|
this.revision = revision;
|
2008-01-04 04:45:43 +00:00
|
|
|
this.user_revisions = new Array(); // cache for this note's list of revisions, loaded from the server on-demand
|
2008-01-22 20:29:29 +00:00
|
|
|
this.read_write = read_write; // whether the user has read-write access to this Editor
|
2008-10-27 23:23:27 +00:00
|
|
|
this.own_notes_only = own_notes_only; // whether the user only has read-write access to their own notes
|
2008-01-22 20:29:29 +00:00
|
|
|
this.edit_enabled = read_write && !deleted_from_id; // whether editing is actually enabled for this Editor
|
|
|
|
this.startup = startup || false; // whether this Editor is for a startup note
|
2007-07-16 20:22:38 +00:00
|
|
|
this.init_highlight = highlight || false;
|
|
|
|
this.init_focus = focus || false;
|
2007-09-05 18:49:28 +00:00
|
|
|
this.closed = false;
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2008-06-13 22:55:19 +00:00
|
|
|
this.hover_target = null;
|
|
|
|
this.hover_timer = null;
|
2007-07-16 20:22:38 +00:00
|
|
|
this.document = null;
|
2008-12-02 19:42:57 +00:00
|
|
|
this.iframe = null;
|
|
|
|
this.div = null;
|
2007-10-22 20:52:04 +00:00
|
|
|
this.title = "";
|
2007-07-16 20:22:38 +00:00
|
|
|
|
2009-01-22 20:08:20 +00:00
|
|
|
// all editors share a common design mode iframe, each claiming use of it as necessary
|
|
|
|
if ( !Editor.shared_iframe )
|
|
|
|
Editor.shared_iframe = new Shared_iframe();
|
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
this.create_div( position_after );
|
|
|
|
|
2008-12-02 19:42:57 +00:00
|
|
|
// if the Editor is to be focused, create an editable iframe. otherwise just create a static div
|
2009-01-10 00:43:33 +00:00
|
|
|
if ( ( highlight || focus ) && this.edit_enabled )
|
2009-01-21 21:33:11 +00:00
|
|
|
this.claim_iframe( position_after );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.create_div = function ( position_after ) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// if there is already a static note div for this Editor, connect up the note controls and bail
|
|
|
|
var static_note_div = getElement( "static_note_" + this.id );
|
|
|
|
if ( static_note_div ) {
|
|
|
|
this.note_controls = getElement( "note_controls_" + this.id );
|
2009-01-23 22:55:57 +00:00
|
|
|
this.holder = getElement( "note_holder_" + this.id );
|
2009-02-05 01:09:54 +00:00
|
|
|
this.grabber = getElement( "note_grabber_" + this.id );
|
2009-01-21 21:33:11 +00:00
|
|
|
this.connect_note_controls( true );
|
|
|
|
this.div = static_note_div;
|
2009-01-27 19:53:50 +00:00
|
|
|
this.div.editor = this;
|
2009-01-26 20:34:33 +00:00
|
|
|
static_contents = getFirstElementByTagAndClassName( "span", "static_note_contents", this.div );
|
|
|
|
if ( static_contents && static_contents.innerHTML != this.initial_text )
|
|
|
|
static_contents.innerHTML = this.initial_text;
|
2009-01-21 21:33:11 +00:00
|
|
|
this.scrape_title();
|
|
|
|
this.focus_default_text_field();
|
|
|
|
this.connect_handlers();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var static_contents = createDOM( "span", { "class": "static_note_contents" } );
|
|
|
|
static_contents.innerHTML = this.contents();
|
|
|
|
this.div = createDOM(
|
|
|
|
"div", { "class": "static_note_div", "id": "static_note_" + this.id }, static_contents
|
|
|
|
);
|
|
|
|
this.div.editor = this;
|
|
|
|
|
|
|
|
this.create_note_controls();
|
|
|
|
this.connect_note_controls();
|
|
|
|
|
2009-02-05 01:09:54 +00:00
|
|
|
this.holder = createDOM( "table", { "id": "note_holder_" + this.id, "class": "note_holder" },
|
2009-02-12 22:03:54 +00:00
|
|
|
createDOM( "tbody", {},
|
|
|
|
createDOM( "tr", {},
|
|
|
|
createDOM( "td", {}, this.note_controls )
|
|
|
|
),
|
|
|
|
createDOM( "tr", {},
|
|
|
|
createDOM( "td", { "width": "100%" }, this.div ),
|
|
|
|
this.grabber
|
|
|
|
),
|
|
|
|
createDOM( "tr", { "id": "note_shadow_" + this.id, "class": "note_shadow undisplayed" },
|
|
|
|
createDOM( "div", { "class": "note_shadow_corner" } )
|
|
|
|
)
|
2009-02-05 01:09:54 +00:00
|
|
|
)
|
2009-01-21 21:33:11 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if ( position_after && position_after.parentNode )
|
2009-01-23 22:55:57 +00:00
|
|
|
insertSiblingNodesAfter( position_after, this.holder );
|
2008-12-02 19:42:57 +00:00
|
|
|
else
|
2009-01-23 22:55:57 +00:00
|
|
|
appendChildNodes( "notes", this.holder );
|
2009-01-21 21:33:11 +00:00
|
|
|
|
|
|
|
this.scrape_title();
|
|
|
|
this.focus_default_text_field();
|
|
|
|
this.connect_handlers();
|
|
|
|
|
|
|
|
signal( self, "init_complete" );
|
2008-12-02 19:42:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.create_note_controls = function () {
|
|
|
|
var iframe_id = "note_" + this.id;
|
2009-02-05 07:43:54 +00:00
|
|
|
|
2009-02-12 11:15:39 +00:00
|
|
|
this.grabber = createDOM( "td", { "id": "note_grabber_" + this.id, "class": "note_grabber" }, "....." );
|
|
|
|
if ( !this.read_write || this.own_notes_only )
|
|
|
|
addElementClass( this.grabber, "invisible" );
|
|
|
|
|
|
|
|
if ( this.read_write ) {
|
2007-07-16 20:22:38 +00:00
|
|
|
this.delete_button = createDOM( "input", {
|
|
|
|
"type": "button",
|
2007-07-17 01:21:31 +00:00
|
|
|
"class": "note_button",
|
2007-07-16 20:22:38 +00:00
|
|
|
"id": "delete_" + iframe_id,
|
Merged revisions 401-446 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/branches/postgres
................
r402 | witten | 2007-10-04 00:48:49 -0700 (Thu, 04 Oct 2007) | 3 lines
Initialized merge tracking via "svnmerge" with revisions "1-401" from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
................
r404 | witten | 2007-10-04 01:17:07 -0700 (Thu, 04 Oct 2007) | 2 lines
Beginning a conversion from bsddb to postgres.
................
r405 | witten | 2007-10-04 01:18:58 -0700 (Thu, 04 Oct 2007) | 9 lines
Merged revisions 402-404 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
........
r403 | witten | 2007-10-04 01:14:45 -0700 (Thu, 04 Oct 2007) | 2 lines
Yay, no more stupid deprecation warnings from simplejson about the sre module.
........
................
r406 | witten | 2007-10-04 15:34:39 -0700 (Thu, 04 Oct 2007) | 4 lines
* Switched back to Python 2.4 because many Python modules in Debian are not packaged to work with Python 2.5
* Began removal of all references to Scheduler, @async, yield, and so on.
* Converted Database.py to support PostgreSQL and updated its unit tests accordingly.
................
r407 | witten | 2007-10-04 16:34:01 -0700 (Thu, 04 Oct 2007) | 2 lines
All unit tests for the new model classes now pass.
................
r409 | witten | 2007-10-05 00:53:56 -0700 (Fri, 05 Oct 2007) | 2 lines
Reordering some columns and adding some indices.
................
r410 | witten | 2007-10-05 16:08:37 -0700 (Fri, 05 Oct 2007) | 4 lines
Now adding trash notebooks to user_notebook table. Also switching db
conversion/verification tools back to require Python 2.5, since they still use
the old Scheduler, which requires 2.5 generator features.
................
r411 | witten | 2007-10-06 16:26:56 -0700 (Sat, 06 Oct 2007) | 2 lines
Lots more unit tests passing. Most of the recent work was on controller.Users and related stuff.
................
r412 | witten | 2007-10-07 01:52:12 -0700 (Sun, 07 Oct 2007) | 2 lines
controller.Users unit tests now finally pass!
................
r413 | witten | 2007-10-07 02:14:10 -0700 (Sun, 07 Oct 2007) | 3 lines
Got controller.Root unit tests passing.
Moved fake sql_* function shenanigans from Test_users.py to Test_controller.py, for use by other controller unit tests.
................
r414 | witten | 2007-10-08 23:11:11 -0700 (Mon, 08 Oct 2007) | 2 lines
All unit tests pass! Fuck yeah!
................
r415 | witten | 2007-10-08 23:13:07 -0700 (Mon, 08 Oct 2007) | 2 lines
Removing all references to Scheduler from luminotes.py
................
r416 | witten | 2007-10-08 23:54:51 -0700 (Mon, 08 Oct 2007) | 3 lines
Converted deleted_from to deleted_from_id in a few more places.
Fixed bug in Users.contents().
................
r417 | witten | 2007-10-09 00:11:59 -0700 (Tue, 09 Oct 2007) | 3 lines
Typo fix in Note sql method.
Adding autocommit flag to Database.next_id() method.
................
r418 | witten | 2007-10-09 00:13:19 -0700 (Tue, 09 Oct 2007) | 2 lines
Updating unit test for new auto commit flag.
................
r419 | witten | 2007-10-09 00:14:09 -0700 (Tue, 09 Oct 2007) | 2 lines
Removing debugging print.
................
r420 | witten | 2007-10-09 00:20:55 -0700 (Tue, 09 Oct 2007) | 2 lines
More sql fixes. I really need some funtional tests that hit the database and exercise the SQL.
................
r421 | witten | 2007-10-09 00:51:34 -0700 (Tue, 09 Oct 2007) | 3 lines
Fixed controller.Database handling of tuple as an Object_type.
Made SQL for user storage calculation better at handling null values and also more succinct.
................
r422 | witten | 2007-10-09 13:32:16 -0700 (Tue, 09 Oct 2007) | 2 lines
Converting Wiki.js to trash_id notebook member instead of trash object.
................
r423 | witten | 2007-10-09 13:42:10 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer displaying "download as html" on the front page, as people see "download" and think they're downloading the software.
................
r424 | witten | 2007-10-09 14:24:40 -0700 (Tue, 09 Oct 2007) | 2 lines
Notebooks.contents() now returns notebooks with correct read-write status.
................
r425 | witten | 2007-10-09 14:32:25 -0700 (Tue, 09 Oct 2007) | 2 lines
Fixed reporting of validation errors to the user. Now says "The blah is missing." instead of just "is missing"
................
r426 | witten | 2007-10-09 17:05:22 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer redirecting to trash notebook upon login.
................
r427 | witten | 2007-10-09 17:20:33 -0700 (Tue, 09 Oct 2007) | 2 lines
Made controller.Database use a connection pool.
................
r429 | witten | 2007-10-09 20:13:30 -0700 (Tue, 09 Oct 2007) | 2 lines
Converted initdb.py and updatedb.py to Postgres from bsddb.
................
r430 | witten | 2007-10-09 20:37:14 -0700 (Tue, 09 Oct 2007) | 2 lines
Changing error message to remove underscores from variable names.
................
r431 | witten | 2007-10-10 13:23:30 -0700 (Wed, 10 Oct 2007) | 2 lines
Removing unused note_title parameter from Wiki.create_editor().
................
r432 | witten | 2007-10-10 13:25:16 -0700 (Wed, 10 Oct 2007) | 2 lines
Revision regular expression now supports timezone notation.
................
r433 | witten | 2007-10-10 14:43:47 -0700 (Wed, 10 Oct 2007) | 2 lines
Finished implementing ranked ordering for startup notes. (However, there's no way to change the rank from the client yet.)
................
r434 | witten | 2007-10-10 16:25:19 -0700 (Wed, 10 Oct 2007) | 4 lines
More strict access checking. Fixed oversight in Postgres DB conversion where,
in certain controller.Notebook methods, access was only checked at the
notebook level, not at the note level as well.
................
r435 | witten | 2007-10-10 17:45:18 -0700 (Wed, 10 Oct 2007) | 3 lines
Now loading revisions on demand from client when the "changes" button is clicked. Also caching
loading revisions so subsequent clicks don't have to reload.
................
r436 | witten | 2007-10-10 21:31:20 -0700 (Wed, 10 Oct 2007) | 2 lines
Tweaking some of the error handling in Expose and Root so that unhandled errors give a generic error message to the client.
................
r437 | witten | 2007-10-10 21:33:49 -0700 (Wed, 10 Oct 2007) | 2 lines
The release script no longer runs initdb.py, because the default database is no longer a single file included in the tarball.
................
r438 | witten | 2007-10-10 21:40:11 -0700 (Wed, 10 Oct 2007) | 2 lines
Updated install instructuctions to include use of initdb.py.
................
r439 | witten | 2007-10-10 21:56:42 -0700 (Wed, 10 Oct 2007) | 3 lines
Made initdb.py only nuke (drop tables/views) when given a command-line flag.
Also made install directions more correct.
................
r440 | witten | 2007-10-10 21:58:48 -0700 (Wed, 10 Oct 2007) | 2 lines
IE 6 doesn't like commas.
................
r441 | witten | 2007-10-10 22:08:50 -0700 (Wed, 10 Oct 2007) | 4 lines
load your notebook. without clicking on "changes", edit a note that has previous revisions. click on "changes". it'll only show
the most recent revision. fixed by not appending to changes as a result of a save unless the client-side revisions list cache has
something in it
................
r442 | witten | 2007-10-10 23:30:41 -0700 (Wed, 10 Oct 2007) | 2 lines
Forgot to actually save off the new revision as editor.revision.
................
r443 | witten | 2007-10-11 01:35:54 -0700 (Thu, 11 Oct 2007) | 13 lines
More intelligent datetime handling:
* convertdb.py assumes old bsddb database timestamps are Pacific, and then
converts them to UTC before inserting them into the new PostgreSQL
database.
* No longer using naked timezoneless datetime objects in model/controller
code, except in unit tests that need compatability with pysqlite. Now
using UTC everwhere.
* Asking PostgreSQL to give us all timestamps back in UTC.
* New dependency on python-tz (pytz) package, noted in INSTALL doc.
* Client now responsible for converting UTC timestamps to local time for
display.
................
r444 | witten | 2007-10-11 01:46:09 -0700 (Thu, 11 Oct 2007) | 2 lines
Tweak to prevent potential race in IE.
................
r445 | witten | 2007-10-11 01:49:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Got JavaScript "unit" tests passing again.
................
r446 | witten | 2007-10-11 01:53:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Noting that js tests require the Luminotes server on localhost.
................
2007-10-11 09:03:43 +00:00
|
|
|
"value": "delete" + ( this.deleted_from_id ? " forever" : "" ),
|
2007-07-17 01:21:31 +00:00
|
|
|
"title": "delete note [ctrl-d]"
|
2007-07-16 20:22:38 +00:00
|
|
|
} );
|
|
|
|
|
Merged revisions 401-446 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/branches/postgres
................
r402 | witten | 2007-10-04 00:48:49 -0700 (Thu, 04 Oct 2007) | 3 lines
Initialized merge tracking via "svnmerge" with revisions "1-401" from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
................
r404 | witten | 2007-10-04 01:17:07 -0700 (Thu, 04 Oct 2007) | 2 lines
Beginning a conversion from bsddb to postgres.
................
r405 | witten | 2007-10-04 01:18:58 -0700 (Thu, 04 Oct 2007) | 9 lines
Merged revisions 402-404 via svnmerge from
svn+ssh://torsion.org/home/luminotes/repos/luminotes/trunk
........
r403 | witten | 2007-10-04 01:14:45 -0700 (Thu, 04 Oct 2007) | 2 lines
Yay, no more stupid deprecation warnings from simplejson about the sre module.
........
................
r406 | witten | 2007-10-04 15:34:39 -0700 (Thu, 04 Oct 2007) | 4 lines
* Switched back to Python 2.4 because many Python modules in Debian are not packaged to work with Python 2.5
* Began removal of all references to Scheduler, @async, yield, and so on.
* Converted Database.py to support PostgreSQL and updated its unit tests accordingly.
................
r407 | witten | 2007-10-04 16:34:01 -0700 (Thu, 04 Oct 2007) | 2 lines
All unit tests for the new model classes now pass.
................
r409 | witten | 2007-10-05 00:53:56 -0700 (Fri, 05 Oct 2007) | 2 lines
Reordering some columns and adding some indices.
................
r410 | witten | 2007-10-05 16:08:37 -0700 (Fri, 05 Oct 2007) | 4 lines
Now adding trash notebooks to user_notebook table. Also switching db
conversion/verification tools back to require Python 2.5, since they still use
the old Scheduler, which requires 2.5 generator features.
................
r411 | witten | 2007-10-06 16:26:56 -0700 (Sat, 06 Oct 2007) | 2 lines
Lots more unit tests passing. Most of the recent work was on controller.Users and related stuff.
................
r412 | witten | 2007-10-07 01:52:12 -0700 (Sun, 07 Oct 2007) | 2 lines
controller.Users unit tests now finally pass!
................
r413 | witten | 2007-10-07 02:14:10 -0700 (Sun, 07 Oct 2007) | 3 lines
Got controller.Root unit tests passing.
Moved fake sql_* function shenanigans from Test_users.py to Test_controller.py, for use by other controller unit tests.
................
r414 | witten | 2007-10-08 23:11:11 -0700 (Mon, 08 Oct 2007) | 2 lines
All unit tests pass! Fuck yeah!
................
r415 | witten | 2007-10-08 23:13:07 -0700 (Mon, 08 Oct 2007) | 2 lines
Removing all references to Scheduler from luminotes.py
................
r416 | witten | 2007-10-08 23:54:51 -0700 (Mon, 08 Oct 2007) | 3 lines
Converted deleted_from to deleted_from_id in a few more places.
Fixed bug in Users.contents().
................
r417 | witten | 2007-10-09 00:11:59 -0700 (Tue, 09 Oct 2007) | 3 lines
Typo fix in Note sql method.
Adding autocommit flag to Database.next_id() method.
................
r418 | witten | 2007-10-09 00:13:19 -0700 (Tue, 09 Oct 2007) | 2 lines
Updating unit test for new auto commit flag.
................
r419 | witten | 2007-10-09 00:14:09 -0700 (Tue, 09 Oct 2007) | 2 lines
Removing debugging print.
................
r420 | witten | 2007-10-09 00:20:55 -0700 (Tue, 09 Oct 2007) | 2 lines
More sql fixes. I really need some funtional tests that hit the database and exercise the SQL.
................
r421 | witten | 2007-10-09 00:51:34 -0700 (Tue, 09 Oct 2007) | 3 lines
Fixed controller.Database handling of tuple as an Object_type.
Made SQL for user storage calculation better at handling null values and also more succinct.
................
r422 | witten | 2007-10-09 13:32:16 -0700 (Tue, 09 Oct 2007) | 2 lines
Converting Wiki.js to trash_id notebook member instead of trash object.
................
r423 | witten | 2007-10-09 13:42:10 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer displaying "download as html" on the front page, as people see "download" and think they're downloading the software.
................
r424 | witten | 2007-10-09 14:24:40 -0700 (Tue, 09 Oct 2007) | 2 lines
Notebooks.contents() now returns notebooks with correct read-write status.
................
r425 | witten | 2007-10-09 14:32:25 -0700 (Tue, 09 Oct 2007) | 2 lines
Fixed reporting of validation errors to the user. Now says "The blah is missing." instead of just "is missing"
................
r426 | witten | 2007-10-09 17:05:22 -0700 (Tue, 09 Oct 2007) | 2 lines
No longer redirecting to trash notebook upon login.
................
r427 | witten | 2007-10-09 17:20:33 -0700 (Tue, 09 Oct 2007) | 2 lines
Made controller.Database use a connection pool.
................
r429 | witten | 2007-10-09 20:13:30 -0700 (Tue, 09 Oct 2007) | 2 lines
Converted initdb.py and updatedb.py to Postgres from bsddb.
................
r430 | witten | 2007-10-09 20:37:14 -0700 (Tue, 09 Oct 2007) | 2 lines
Changing error message to remove underscores from variable names.
................
r431 | witten | 2007-10-10 13:23:30 -0700 (Wed, 10 Oct 2007) | 2 lines
Removing unused note_title parameter from Wiki.create_editor().
................
r432 | witten | 2007-10-10 13:25:16 -0700 (Wed, 10 Oct 2007) | 2 lines
Revision regular expression now supports timezone notation.
................
r433 | witten | 2007-10-10 14:43:47 -0700 (Wed, 10 Oct 2007) | 2 lines
Finished implementing ranked ordering for startup notes. (However, there's no way to change the rank from the client yet.)
................
r434 | witten | 2007-10-10 16:25:19 -0700 (Wed, 10 Oct 2007) | 4 lines
More strict access checking. Fixed oversight in Postgres DB conversion where,
in certain controller.Notebook methods, access was only checked at the
notebook level, not at the note level as well.
................
r435 | witten | 2007-10-10 17:45:18 -0700 (Wed, 10 Oct 2007) | 3 lines
Now loading revisions on demand from client when the "changes" button is clicked. Also caching
loading revisions so subsequent clicks don't have to reload.
................
r436 | witten | 2007-10-10 21:31:20 -0700 (Wed, 10 Oct 2007) | 2 lines
Tweaking some of the error handling in Expose and Root so that unhandled errors give a generic error message to the client.
................
r437 | witten | 2007-10-10 21:33:49 -0700 (Wed, 10 Oct 2007) | 2 lines
The release script no longer runs initdb.py, because the default database is no longer a single file included in the tarball.
................
r438 | witten | 2007-10-10 21:40:11 -0700 (Wed, 10 Oct 2007) | 2 lines
Updated install instructuctions to include use of initdb.py.
................
r439 | witten | 2007-10-10 21:56:42 -0700 (Wed, 10 Oct 2007) | 3 lines
Made initdb.py only nuke (drop tables/views) when given a command-line flag.
Also made install directions more correct.
................
r440 | witten | 2007-10-10 21:58:48 -0700 (Wed, 10 Oct 2007) | 2 lines
IE 6 doesn't like commas.
................
r441 | witten | 2007-10-10 22:08:50 -0700 (Wed, 10 Oct 2007) | 4 lines
load your notebook. without clicking on "changes", edit a note that has previous revisions. click on "changes". it'll only show
the most recent revision. fixed by not appending to changes as a result of a save unless the client-side revisions list cache has
something in it
................
r442 | witten | 2007-10-10 23:30:41 -0700 (Wed, 10 Oct 2007) | 2 lines
Forgot to actually save off the new revision as editor.revision.
................
r443 | witten | 2007-10-11 01:35:54 -0700 (Thu, 11 Oct 2007) | 13 lines
More intelligent datetime handling:
* convertdb.py assumes old bsddb database timestamps are Pacific, and then
converts them to UTC before inserting them into the new PostgreSQL
database.
* No longer using naked timezoneless datetime objects in model/controller
code, except in unit tests that need compatability with pysqlite. Now
using UTC everwhere.
* Asking PostgreSQL to give us all timestamps back in UTC.
* New dependency on python-tz (pytz) package, noted in INSTALL doc.
* Client now responsible for converting UTC timestamps to local time for
display.
................
r444 | witten | 2007-10-11 01:46:09 -0700 (Thu, 11 Oct 2007) | 2 lines
Tweak to prevent potential race in IE.
................
r445 | witten | 2007-10-11 01:49:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Got JavaScript "unit" tests passing again.
................
r446 | witten | 2007-10-11 01:53:58 -0700 (Thu, 11 Oct 2007) | 2 lines
Noting that js tests require the Luminotes server on localhost.
................
2007-10-11 09:03:43 +00:00
|
|
|
if ( this.deleted_from_id ) {
|
2007-08-07 01:48:43 +00:00
|
|
|
this.undelete_button = createDOM( "input", {
|
|
|
|
"type": "button",
|
|
|
|
"class": "note_button",
|
|
|
|
"id": "undelete_" + iframe_id,
|
|
|
|
"value": "undelete",
|
|
|
|
"title": "undelete note"
|
|
|
|
} );
|
2008-12-02 19:42:57 +00:00
|
|
|
} else if ( !this.own_notes_only ) {
|
2007-11-05 21:36:39 +00:00
|
|
|
this.changes_button = createDOM( "input", {
|
|
|
|
"type": "button",
|
|
|
|
"class": "note_button",
|
|
|
|
"id": "changes_" + iframe_id,
|
|
|
|
"value": "changes",
|
|
|
|
"title": "previous revisions"
|
|
|
|
} );
|
|
|
|
|
2009-03-03 03:39:02 +00:00
|
|
|
this.tools_button = createDOM( "input", {
|
2008-11-15 06:52:54 +00:00
|
|
|
"type": "button",
|
|
|
|
"class": "note_button",
|
2009-03-03 03:39:02 +00:00
|
|
|
"id": "tools_" + iframe_id,
|
|
|
|
"value": "tools",
|
|
|
|
"title": "note tools"
|
2008-11-15 06:52:54 +00:00
|
|
|
} );
|
2007-08-07 01:48:43 +00:00
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2008-12-02 19:42:57 +00:00
|
|
|
if ( !this.deleted_from_id && ( this.read_write || !this.startup ) && !this.own_notes_only ) {
|
2007-07-16 20:22:38 +00:00
|
|
|
this.hide_button = createDOM( "input", {
|
|
|
|
"type": "button",
|
2007-07-17 01:21:31 +00:00
|
|
|
"class": "note_button",
|
2007-07-16 20:22:38 +00:00
|
|
|
"id": "hide_" + iframe_id,
|
|
|
|
"value": "hide",
|
2007-07-17 01:21:31 +00:00
|
|
|
"title": "hide note [ctrl-h]"
|
2007-07-16 20:22:38 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2008-12-02 19:42:57 +00:00
|
|
|
this.note_controls = createDOM(
|
|
|
|
"div", { "class": "note_controls", "id": "note_controls_" + this.id },
|
2007-07-16 20:22:38 +00:00
|
|
|
this.delete_button ? this.delete_button : null,
|
|
|
|
this.delete_button ? " " : null,
|
2007-07-26 01:18:41 +00:00
|
|
|
this.changes_button ? this.changes_button : null,
|
|
|
|
this.changes_button ? " " : null,
|
2009-03-03 03:39:02 +00:00
|
|
|
this.tools_button ? this.tools_button : null,
|
|
|
|
this.tools_button ? " " : null,
|
2007-08-07 01:48:43 +00:00
|
|
|
this.undelete_button ? this.undelete_button : null,
|
|
|
|
this.undelete_button ? " " : null,
|
2007-07-16 20:22:38 +00:00
|
|
|
this.hide_button ? this.hide_button : null
|
|
|
|
);
|
2008-12-02 19:42:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.connect_note_controls = function ( store_control_buttons ) {
|
|
|
|
if ( store_control_buttons ) {
|
|
|
|
var iframe_id = "note_" + this.id;
|
|
|
|
this.delete_button = getElement( "delete_" + iframe_id );
|
|
|
|
this.undelete_button = getElement( "undelete_" + iframe_id );
|
|
|
|
this.changes_button = getElement( "changes_" + iframe_id );
|
2009-03-03 03:39:02 +00:00
|
|
|
this.tools_button = getElement( "tools_" + iframe_id );
|
2008-12-02 19:42:57 +00:00
|
|
|
this.hide_button = getElement( "hide_" + iframe_id );
|
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
|
2008-12-02 19:42:57 +00:00
|
|
|
var self = this;
|
2009-01-13 00:44:28 +00:00
|
|
|
if ( this.delete_button ) {
|
|
|
|
disconnectAll( this.delete_button );
|
2008-12-02 19:42:57 +00:00
|
|
|
connect( this.delete_button, "onclick", function ( event ) { signal( self, "delete_clicked", event ); } );
|
2009-01-13 00:44:28 +00:00
|
|
|
}
|
|
|
|
if ( this.undelete_button ) {
|
|
|
|
disconnectAll( this.undelete_button );
|
2008-12-02 19:42:57 +00:00
|
|
|
connect( this.undelete_button, "onclick", function ( event ) { signal( self, "undelete_clicked", event ); } );
|
2009-01-13 00:44:28 +00:00
|
|
|
}
|
|
|
|
if ( this.changes_button ) {
|
|
|
|
disconnectAll( this.changes_button );
|
2008-12-02 19:42:57 +00:00
|
|
|
connect( this.changes_button, "onclick", function ( event ) { signal( self, "changes_clicked", event ); } );
|
2009-01-13 00:44:28 +00:00
|
|
|
}
|
2009-03-03 03:39:02 +00:00
|
|
|
if ( this.tools_button ) {
|
|
|
|
disconnectAll( this.tools_button );
|
|
|
|
connect( this.tools_button, "onclick", function ( event ) { signal( self, "tools_clicked", event ); } );
|
2009-01-13 00:44:28 +00:00
|
|
|
}
|
|
|
|
if ( this.hide_button ) {
|
|
|
|
disconnectAll( this.hide_button );
|
2008-12-02 19:42:57 +00:00
|
|
|
connect( this.hide_button, "onclick", function ( event ) { signal( self, "hide_clicked", event ); } );
|
2009-01-13 00:44:28 +00:00
|
|
|
}
|
2009-02-06 22:01:44 +00:00
|
|
|
|
|
|
|
if ( this.grabber ) {
|
|
|
|
disconnectAll( this.grabber );
|
2009-02-06 22:54:02 +00:00
|
|
|
connect( this.grabber, "onmousedown", function ( event ) { self.start_drag( event ); } );
|
2009-02-11 09:05:00 +00:00
|
|
|
connect( this.grabber, "onclick", function ( event ) { event.stop(); } );
|
2009-02-06 22:01:44 +00:00
|
|
|
}
|
2008-12-02 19:42:57 +00:00
|
|
|
}
|
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
Editor.prototype.claim_iframe = function ( position_after ) {
|
2009-01-21 21:33:11 +00:00
|
|
|
var self = this;
|
2008-12-02 19:42:57 +00:00
|
|
|
var iframe_id = "note_" + this.id;
|
2009-01-03 23:52:12 +00:00
|
|
|
|
|
|
|
// if there is already an iframe for this Editor, bail
|
2009-01-23 00:54:56 +00:00
|
|
|
if ( this.iframe )
|
2009-01-03 23:52:12 +00:00
|
|
|
return;
|
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
// claim the reusable iframe for this note, stealing it from the note that's using it (if any)
|
2009-01-22 20:08:20 +00:00
|
|
|
this.iframe = Editor.shared_iframe.iframe;
|
2009-01-21 21:33:11 +00:00
|
|
|
this.iframe.setAttribute( "id", iframe_id );
|
|
|
|
this.iframe.setAttribute( "name", iframe_id );
|
2009-01-09 20:27:00 +00:00
|
|
|
|
2009-01-23 00:54:56 +00:00
|
|
|
if ( this.iframe.editor )
|
2009-01-23 06:33:32 +00:00
|
|
|
this.iframe.editor.release_iframe();
|
2009-01-23 00:54:56 +00:00
|
|
|
this.iframe.editor = this;
|
2009-01-09 20:27:00 +00:00
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
// setup the note controls
|
|
|
|
this.note_controls = getElement( "note_controls_" + this.id );
|
|
|
|
this.connect_note_controls( true );
|
2009-01-09 20:27:00 +00:00
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
// give the iframe the note's current contents and then resize it based on the size of the div
|
|
|
|
var range = this.add_selection_bookmark();
|
|
|
|
this.set_iframe_contents( this.contents() );
|
|
|
|
this.remove_selection_bookmark( range );
|
2009-01-22 21:05:18 +00:00
|
|
|
this.resize( true );
|
2008-12-02 19:42:57 +00:00
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
// make the completed iframe visible and hide the static div
|
|
|
|
addElementClass( this.iframe, "focused_note_frame" );
|
|
|
|
removeElementClass( this.iframe, "invisible" );
|
|
|
|
addElementClass( this.div, "invisible" );
|
2009-02-05 01:09:54 +00:00
|
|
|
addElementClass( this.grabber, "note_grabber_focused" );
|
2009-01-07 20:39:48 +00:00
|
|
|
|
2009-01-15 00:37:02 +00:00
|
|
|
function finish_init() {
|
2009-01-24 21:57:33 +00:00
|
|
|
self.position_cursor( range );
|
2009-01-15 00:37:02 +00:00
|
|
|
self.connect_handlers();
|
2009-01-22 22:23:07 +00:00
|
|
|
signal( self, "focused", self );
|
2009-01-24 22:21:07 +00:00
|
|
|
self.focus( true );
|
2009-01-15 00:37:02 +00:00
|
|
|
}
|
|
|
|
|
2009-01-24 22:21:07 +00:00
|
|
|
// this small delay gives Firefox enough lag time after set_iframe_contents() to display the
|
|
|
|
// blinking text cursor at the end of the iframe contents
|
|
|
|
if ( GECKO && !range )
|
|
|
|
setTimeout( finish_init, 1 );
|
|
|
|
else
|
|
|
|
finish_init();
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 20:27:00 +00:00
|
|
|
Editor.prototype.set_iframe_contents = function ( contents_text ) {
|
|
|
|
if ( this.iframe.contentDocument ) { // browsers such as Firefox
|
|
|
|
this.document = this.iframe.contentDocument;
|
|
|
|
} else { // browsers such as IE
|
|
|
|
this.document = this.iframe.contentWindow.document;
|
|
|
|
}
|
|
|
|
|
2009-01-24 22:21:07 +00:00
|
|
|
// hack: add a zero-width space to make the horizontal line under title show up in the
|
|
|
|
// correct position, even before there is a title
|
|
|
|
if ( !contents_text )
|
2009-01-09 20:27:00 +00:00
|
|
|
contents_text = "<h3>​";
|
|
|
|
|
2009-01-24 22:21:07 +00:00
|
|
|
this.document.body.innerHTML = contents_text;
|
2009-01-09 20:27:00 +00:00
|
|
|
}
|
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
Editor.prototype.focus_default_text_field = function () {
|
|
|
|
// special-case: focus any username field found within this div
|
|
|
|
var username = getElement( "username" );
|
|
|
|
if ( username && isChildNode( username, this.div ) )
|
|
|
|
username.focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.add_selection_bookmark = function () {
|
2009-01-24 21:57:33 +00:00
|
|
|
if ( window.getSelection ) { // browsers such as Firefox
|
|
|
|
// grab the current range for this editor's div so that it can be duplicated within the iframe
|
|
|
|
var selection = window.getSelection();
|
|
|
|
if ( selection.rangeCount > 0 )
|
|
|
|
var range = selection.getRangeAt( 0 );
|
|
|
|
else
|
|
|
|
var range = document.createRange();
|
2009-01-24 20:25:48 +00:00
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
// if the current range is not within this editor's static note div, then bail
|
|
|
|
if ( range.startContainer == document || range.endContainer == document )
|
|
|
|
return null;
|
2009-02-11 01:05:03 +00:00
|
|
|
try {
|
|
|
|
if ( !isChildNode( range.startContainer.parentNode, this.div ) || !isChildNode( range.endContainer.parentNode, this.div ) )
|
|
|
|
return null;
|
|
|
|
} catch ( e ) {
|
2009-01-24 21:57:33 +00:00
|
|
|
return null;
|
2009-02-11 01:05:03 +00:00
|
|
|
}
|
2009-01-21 21:33:11 +00:00
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
// mark the nodes that are start and end containers for the current range. we have to mark the
|
|
|
|
// parent node instead of the start/end container itself, because text nodes can't have classes
|
|
|
|
var parent_node = range.startContainer.parentNode
|
2009-01-26 21:58:00 +00:00
|
|
|
if ( !hasElementClass( parent_node, "static_note_div" ) ) {
|
|
|
|
addElementClass( parent_node, "range_start_container" );
|
|
|
|
for ( var i in parent_node.childNodes ) {
|
|
|
|
var child_node = parent_node.childNodes[ i ];
|
|
|
|
if ( child_node == range.startContainer ) {
|
|
|
|
range.start_child_offset = i;
|
|
|
|
break;
|
|
|
|
}
|
2009-01-26 21:30:26 +00:00
|
|
|
}
|
2009-01-24 21:57:33 +00:00
|
|
|
}
|
2009-01-21 21:33:11 +00:00
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
var parent_node = range.endContainer.parentNode
|
2009-01-26 21:58:00 +00:00
|
|
|
if ( !hasElementClass( parent_node, "static_note_div" ) ) {
|
|
|
|
addElementClass( parent_node, "range_end_container" );
|
|
|
|
for ( var i in parent_node.childNodes ) {
|
|
|
|
var child_node = parent_node.childNodes[ i ];
|
|
|
|
if ( child_node == range.endContainer ) {
|
|
|
|
range.end_child_offset = i;
|
|
|
|
break;
|
|
|
|
}
|
2009-01-26 21:30:26 +00:00
|
|
|
}
|
2009-01-24 21:57:33 +00:00
|
|
|
}
|
2009-01-21 21:33:11 +00:00
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
return range;
|
|
|
|
} else if ( document.selection ) { // browsers such as IE
|
|
|
|
var range = document.selection.createRange();
|
|
|
|
if ( !isChildNode( range.parentElement(), this.div ) )
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return range;
|
2009-01-21 21:33:11 +00:00
|
|
|
}
|
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
return null;
|
2009-01-21 21:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.remove_selection_bookmark = function ( range ) {
|
|
|
|
// unmark the nodes that are start and end containers for the given range
|
2009-01-24 21:57:33 +00:00
|
|
|
if ( range && range.startContainer && range.endContainer ) {
|
2009-01-21 21:33:11 +00:00
|
|
|
removeElementClass( range.startContainer.parentNode, "range_start_container" );
|
|
|
|
removeElementClass( range.endContainer.parentNode, "range_end_container" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
Editor.prototype.position_cursor = function ( div_range ) {
|
2009-01-15 00:37:02 +00:00
|
|
|
if ( this.init_focus ) {
|
|
|
|
this.init_focus = false;
|
|
|
|
if ( this.iframe )
|
2009-01-22 22:23:07 +00:00
|
|
|
this.focus( true );
|
2009-01-15 00:37:02 +00:00
|
|
|
}
|
|
|
|
|
2009-01-15 09:36:32 +00:00
|
|
|
// if requested, move the text cursor to a specific location
|
2009-01-21 21:33:11 +00:00
|
|
|
if ( div_range && this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
|
|
|
// position the cursor by using a bookmarked text range
|
2009-01-27 06:03:59 +00:00
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
2009-01-24 23:13:44 +00:00
|
|
|
|
2009-01-27 06:03:59 +00:00
|
|
|
selection.removeAllRanges();
|
|
|
|
var range = this.document.createRange();
|
2009-01-21 21:33:11 +00:00
|
|
|
|
|
|
|
var start = getFirstElementByTagAndClassName( null, "range_start_container", this.document );
|
|
|
|
var end = getFirstElementByTagAndClassName( null, "range_end_container", this.document );
|
|
|
|
|
2009-01-23 20:12:38 +00:00
|
|
|
// if start and end don't exist, assume that they are the top-level document body node. this
|
|
|
|
// occurs when start and end are set to the div's static_note_contents node, which doesn't exist
|
|
|
|
// within the iframe
|
|
|
|
if ( !start && !end ) {
|
|
|
|
start = this.document.body;
|
|
|
|
end = this.document.body;
|
|
|
|
}
|
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
if ( start && end ) {
|
|
|
|
removeElementClass( start, "range_start_container" );
|
|
|
|
removeElementClass( end, "range_end_container" );
|
|
|
|
if ( div_range.start_child_offset )
|
|
|
|
start = start.childNodes[ div_range.start_child_offset ];
|
|
|
|
if ( div_range.end_child_offset )
|
|
|
|
end = end.childNodes[ div_range.end_child_offset ];
|
|
|
|
range.setStart( start, div_range.startOffset );
|
|
|
|
range.setEnd( end, div_range.endOffset );
|
|
|
|
selection.addRange( range );
|
|
|
|
return;
|
2009-01-15 09:36:32 +00:00
|
|
|
}
|
2009-01-24 21:57:33 +00:00
|
|
|
} else if ( div_range && this.document.selection ) { // browsers such as IE
|
|
|
|
function text_length( text ) {
|
|
|
|
var count = 0;
|
|
|
|
for ( var i = 0; i < text.length; i++ ) {
|
|
|
|
if ( text.charAt( i ) != "\r" )
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a range that extends from the start of the div to the start of the original range
|
|
|
|
var before_div_range = div_range.duplicate();
|
|
|
|
before_div_range.moveToElementText( this.div );
|
|
|
|
before_div_range.setEndPoint( "EndToStart", div_range );
|
|
|
|
|
|
|
|
// calculate the start offset based on the size of that range in characters
|
|
|
|
var start = text_length( before_div_range.text ) - 1;
|
|
|
|
var end = start + text_length( div_range.text );
|
|
|
|
|
|
|
|
// finally, position the text cursor within the iframe
|
2009-01-21 21:33:11 +00:00
|
|
|
var range = this.document.selection.createRange();
|
2009-01-24 21:57:33 +00:00
|
|
|
range.moveEnd( "character", end );
|
|
|
|
range.moveStart( "character", start );
|
2009-01-21 21:33:11 +00:00
|
|
|
range.select();
|
2009-01-15 09:36:32 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, just move the text cursor to the end of the text
|
2009-02-16 21:41:11 +00:00
|
|
|
if ( this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
2009-01-27 06:03:59 +00:00
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
2009-01-09 20:27:00 +00:00
|
|
|
var last_node = this.document.body.lastChild;
|
2009-01-23 20:12:38 +00:00
|
|
|
|
2009-01-27 06:03:59 +00:00
|
|
|
selection.removeAllRanges();
|
|
|
|
var range = this.document.createRange();
|
2009-01-23 22:25:24 +00:00
|
|
|
|
2009-01-25 07:56:28 +00:00
|
|
|
while ( ( last_node.nodeValue == "\n" || ( last_node.tagName && last_node.tagName == "BR" ) ) &&
|
|
|
|
last_node.previousSibling )
|
2009-01-09 20:27:00 +00:00
|
|
|
last_node = last_node.previousSibling;
|
|
|
|
|
2009-01-23 22:25:24 +00:00
|
|
|
range.selectNodeContents( last_node );
|
|
|
|
range.collapse( false );
|
|
|
|
selection.addRange( range );
|
2009-01-09 20:27:00 +00:00
|
|
|
} else if ( this.document.selection ) { // browsers such as IE
|
|
|
|
var range = this.document.selection.createRange();
|
2009-01-15 00:37:02 +00:00
|
|
|
range.move( "textedit" );
|
|
|
|
range.select();
|
2009-01-09 20:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-22 23:30:17 +00:00
|
|
|
Editor.prototype.position_cursor_after = function ( node ) {
|
|
|
|
if ( this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
selection.selectAllChildren( node );
|
|
|
|
selection.collapseToEnd();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-10 00:43:33 +00:00
|
|
|
Editor.prototype.connect_handlers = function () {
|
2009-01-10 01:15:07 +00:00
|
|
|
if ( this.document && this.document.body ) {
|
2009-01-10 00:43:33 +00:00
|
|
|
// since the browser may subtly tweak the html when it's inserted, save off the browser's version
|
|
|
|
// of the html here. this yields more accurate comparisons within the dirty() method
|
|
|
|
if ( this.start_dirty )
|
|
|
|
this.initial_text = "";
|
|
|
|
else
|
|
|
|
this.initial_text = this.document.body.innerHTML;
|
|
|
|
}
|
2008-05-18 06:17:37 +00:00
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
var self = this; // necessary so that the member functions of this editor object are used
|
2009-01-10 00:43:33 +00:00
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
if ( !this.iframe ) {
|
2009-01-25 07:20:34 +00:00
|
|
|
connect( this.div, "onmouseup", function ( event ) { self.mouse_released( event ); } );
|
|
|
|
connect( this.div, "onclick", function ( event ) { self.mouse_clicked( event ) } );
|
2009-01-10 00:43:33 +00:00
|
|
|
connect( this.div, "onmouseover", function ( event ) { self.mouse_hovered( event ); } );
|
|
|
|
connect( this.div, "ondragover", function ( event ) { self.mouse_dragged( event ); } );
|
|
|
|
} else {
|
|
|
|
if ( this.edit_enabled ) {
|
|
|
|
connect( this.document, "onkeydown", function ( event ) { self.key_pressed( event ); } );
|
|
|
|
connect( this.document, "onkeyup", function ( event ) { self.key_released( event ); } );
|
|
|
|
}
|
2009-01-25 07:20:34 +00:00
|
|
|
connect( this.document, "onmouseup", function ( event ) { self.mouse_released( event ); } );
|
|
|
|
connect( this.document, "onclick", function ( event ) { self.mouse_clicked( event ); } );
|
2009-01-10 00:43:33 +00:00
|
|
|
connect( this.document, "onmouseover", function ( event ) { self.mouse_hovered( event ); } );
|
|
|
|
connect( this.document, "ondragover", function ( event ) { self.mouse_dragged( event ); } );
|
2009-01-21 21:43:45 +00:00
|
|
|
connect( this.iframe, "onload", function () { self.resize(); } );
|
2009-01-21 21:33:11 +00:00
|
|
|
connect( this.iframe, "onresize", function () { setTimeout( function () { self.resize() }, 50 ); } );
|
2009-01-10 00:43:33 +00:00
|
|
|
connect( this.iframe.contentWindow, "onpaste", function ( event ) { setTimeout( function () { self.resize() }, 50 ); } );
|
|
|
|
connect( this.iframe.contentWindow, "oncut", function ( event ) { setTimeout( function () { self.resize() }, 50 ); } );
|
|
|
|
}
|
2008-05-31 00:10:57 +00:00
|
|
|
|
|
|
|
// handle each form submit event by forwarding it on as a custom event
|
2008-06-03 23:12:13 +00:00
|
|
|
function connect_form( form ) {
|
2009-01-13 21:38:35 +00:00
|
|
|
disconnectAll( form );
|
2008-05-31 00:10:57 +00:00
|
|
|
connect( form, "onsubmit", function ( event ) {
|
2008-06-02 21:16:35 +00:00
|
|
|
signal( self, "submit_form", form );
|
2008-05-31 00:10:57 +00:00
|
|
|
event.stop();
|
|
|
|
} );
|
|
|
|
}
|
2007-12-11 01:15:03 +00:00
|
|
|
|
2009-01-23 23:24:47 +00:00
|
|
|
var forms = getElementsByTagAndClassName( "form", null, this.div );
|
2008-06-03 23:12:13 +00:00
|
|
|
for ( var i in forms ) {
|
|
|
|
var form = forms[ i ];
|
|
|
|
connect_form( form );
|
|
|
|
}
|
|
|
|
|
2008-05-31 00:10:57 +00:00
|
|
|
// connect each (non-submit) button to issue an event
|
2008-06-02 21:16:35 +00:00
|
|
|
function connect_button( button ) {
|
2009-01-13 21:38:35 +00:00
|
|
|
disconnectAll( button );
|
2008-05-31 00:10:57 +00:00
|
|
|
connect( button, "onclick", function ( event ) {
|
|
|
|
signal( self, "button_clicked", this, button );
|
|
|
|
event.stop();
|
|
|
|
} );
|
|
|
|
}
|
2007-12-11 01:15:03 +00:00
|
|
|
|
2009-01-23 23:24:47 +00:00
|
|
|
var buttons = getElementsByTagAndClassName( "input", "button", this.div );
|
2008-06-02 21:16:35 +00:00
|
|
|
for ( var i in buttons ) {
|
|
|
|
var button = buttons[ i ];
|
2009-01-24 20:25:48 +00:00
|
|
|
if ( button.getAttribute( "type" ) == "submit" )
|
2008-06-02 21:16:35 +00:00
|
|
|
continue;
|
2008-05-08 03:05:35 +00:00
|
|
|
|
2008-06-02 21:16:35 +00:00
|
|
|
connect_button( button );
|
|
|
|
}
|
2007-09-26 23:49:27 +00:00
|
|
|
|
2007-10-19 20:55:13 +00:00
|
|
|
// browsers such as Firefox, but not Opera
|
2009-01-10 00:43:33 +00:00
|
|
|
if ( !OPERA && this.iframe && this.iframe.contentDocument && this.edit_enabled ) {
|
2007-10-19 20:55:13 +00:00
|
|
|
this.exec_command( "styleWithCSS", false );
|
2008-08-12 03:37:20 +00:00
|
|
|
this.exec_command( "insertbronreturn", true );
|
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
|
2009-01-13 00:24:52 +00:00
|
|
|
if ( this.init_highlight ) {
|
2009-01-12 22:19:32 +00:00
|
|
|
this.highlight();
|
2009-01-13 00:24:52 +00:00
|
|
|
this.init_highlight = false;
|
|
|
|
}
|
2007-09-27 05:45:43 +00:00
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
this.scrape_title();
|
2007-11-07 20:44:30 +00:00
|
|
|
|
2007-09-08 00:21:33 +00:00
|
|
|
signal( self, "init_complete" );
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.highlight = function ( scroll ) {
|
|
|
|
if ( scroll == undefined )
|
|
|
|
scroll = true;
|
|
|
|
|
2008-07-29 21:48:54 +00:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
function do_highlight() {
|
2009-01-23 06:36:24 +00:00
|
|
|
if ( !self.iframe ) {
|
2009-01-09 21:05:42 +00:00
|
|
|
new Highlight( self.div, options = { "queue": { "scope": "highlight", "limit": 1 } } );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-01-10 00:43:33 +00:00
|
|
|
if ( OPERA ) { // MochiKit's Highlight for iframes is broken in Opera
|
2008-07-29 21:48:54 +00:00
|
|
|
pulsate( self.iframe, options = { "pulses": 1, "duration": 0.5 } );
|
|
|
|
} else if ( self.iframe.contentDocument ) { // browsers such as Firefox
|
2009-01-09 21:05:42 +00:00
|
|
|
new Highlight( self.iframe, options = { "queue": { "scope": "highlight", "limit": 1 } } );
|
2008-07-29 21:48:54 +00:00
|
|
|
} else { // browsers such as IE
|
|
|
|
if ( self.document && self.document.body )
|
2009-01-09 21:05:42 +00:00
|
|
|
new Highlight( self.document.body, options = { "queue": { "scope": "highlight", "limit": 1 } } );
|
2008-07-29 21:48:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-11 19:40:26 +00:00
|
|
|
// focusing the highlighted editor before scrolling to it prevents IE from deciding to
|
|
|
|
// automatically scroll back to the link immediately afterwards
|
|
|
|
this.focus();
|
|
|
|
|
2007-11-09 08:24:40 +00:00
|
|
|
if ( scroll ) {
|
2009-01-09 21:05:42 +00:00
|
|
|
var editor_node = this.iframe || this.div;
|
|
|
|
|
2007-11-09 08:24:40 +00:00
|
|
|
// if the editor is already completely on-screen, then there's no need to scroll
|
|
|
|
var viewport_position = getViewportPosition();
|
|
|
|
if ( getElementPosition( this.note_controls ).y < viewport_position.y ||
|
2009-01-09 21:05:42 +00:00
|
|
|
getElementPosition( editor_node ).y + getElementDimensions( editor_node ).h > viewport_position.y + getViewportDimensions().h ) {
|
2008-07-29 21:52:23 +00:00
|
|
|
new ScrollTo( this.note_controls, { "afterFinish": do_highlight, "duration": 0.25 } );
|
2008-07-29 21:48:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-11-09 08:24:40 +00:00
|
|
|
}
|
|
|
|
|
2008-07-29 21:48:54 +00:00
|
|
|
do_highlight();
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.exec_command = function ( command, parameter ) {
|
|
|
|
command = command.toLowerCase();
|
|
|
|
|
|
|
|
if ( command == "h3" ) {
|
|
|
|
if ( this.state_enabled( "h3" ) )
|
2008-08-12 03:37:20 +00:00
|
|
|
this.document.execCommand( "formatblock", false, "<p>" );
|
2007-07-16 20:22:38 +00:00
|
|
|
else
|
|
|
|
this.document.execCommand( "formatblock", false, "<h3>" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.document.execCommand( command, false, parameter );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.insert_html = function ( html ) {
|
|
|
|
if ( html.length == 0 ) return;
|
|
|
|
|
2008-10-03 06:59:39 +00:00
|
|
|
if ( !this.edit_enabled || strip( this.contents() ) == "" ) {
|
2007-07-16 20:22:38 +00:00
|
|
|
this.document.body.innerHTML = html;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try { // browsers supporting insertHTML command, such as Firefox
|
|
|
|
this.document.execCommand( "insertHTML", false, html );
|
|
|
|
} catch ( e ) { // browsers that don't support insertHTML, such as IE
|
|
|
|
this.document.body.innerHTML = html;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-16 02:10:38 +00:00
|
|
|
Editor.prototype.query_command_value = function ( command ) {
|
|
|
|
return this.document.queryCommandValue( command );
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
// resize the editor's frame to fit the dimensions of its content
|
2009-01-22 21:05:18 +00:00
|
|
|
Editor.prototype.resize = function ( get_height_from_div ) {
|
2008-03-12 00:40:02 +00:00
|
|
|
if ( !this.document ) return;
|
2009-01-27 19:53:50 +00:00
|
|
|
|
|
|
|
this.reposition();
|
|
|
|
|
2009-01-22 21:05:18 +00:00
|
|
|
var height = null;
|
2009-02-05 01:09:54 +00:00
|
|
|
var width = elementDimensions( this.div ).w;
|
2008-03-12 00:40:02 +00:00
|
|
|
|
2009-01-27 23:34:17 +00:00
|
|
|
// set the width first, because that influences the height of the content
|
2009-02-05 01:09:54 +00:00
|
|
|
width -= FRAME_BORDER_HEIGHT * 2;
|
2009-01-22 21:05:18 +00:00
|
|
|
var size = { "w": width };
|
|
|
|
setElementDimensions( this.iframe, size );
|
|
|
|
|
2009-01-22 21:16:25 +00:00
|
|
|
if ( get_height_from_div && !this.empty() ) {
|
2009-01-22 21:05:18 +00:00
|
|
|
height = elementDimensions( this.div ).h;
|
2009-01-15 09:36:32 +00:00
|
|
|
height -= FRAME_BORDER_HEIGHT * 2; // 2 pixels at the top and 2 at the bottom
|
2009-01-09 20:27:00 +00:00
|
|
|
// if no height is given, get the height from this editor's document body
|
|
|
|
} else {
|
2009-01-16 03:06:05 +00:00
|
|
|
if ( this.iframe && this.iframe.contentDocument && !WEBKIT ) { // Gecko and other sane browsers
|
2009-01-09 20:27:00 +00:00
|
|
|
height = elementDimensions( this.document.documentElement ).h;
|
|
|
|
} else { // IE
|
|
|
|
height = this.document.body.scrollHeight;
|
|
|
|
}
|
2007-09-27 05:45:43 +00:00
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
|
2009-01-22 21:05:18 +00:00
|
|
|
size = { "h": height };
|
2009-01-22 20:08:20 +00:00
|
|
|
setElementDimensions( this.iframe, size );
|
2009-01-22 21:05:18 +00:00
|
|
|
setElementDimensions( this.div, size );
|
2009-01-27 09:32:21 +00:00
|
|
|
|
|
|
|
var self = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.reposition = function ( repeat ) {
|
2009-01-27 19:53:50 +00:00
|
|
|
if ( !this.iframe ) return;
|
|
|
|
|
2009-01-27 09:32:21 +00:00
|
|
|
// give the iframe the exact same position as the div it replaces. subtract the position of the
|
|
|
|
// center_content_area container, which is relatively positioned
|
|
|
|
var position = getElementPosition( this.div );
|
|
|
|
var orig_position = getElementPosition( this.iframe );
|
|
|
|
if ( repeat && position.x == orig_position.x && position.y == orig_position.y )
|
|
|
|
return;
|
|
|
|
|
|
|
|
var container_position = getElementPosition( "center_content_area" );
|
|
|
|
position.x -= container_position.x;
|
|
|
|
position.y -= container_position.y;
|
|
|
|
|
|
|
|
setElementPosition( this.iframe, position );
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
setTimeout( function () { self.reposition( true ); }, 50 );
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2009-02-06 22:54:02 +00:00
|
|
|
Editor.prototype.start_drag = function ( event ) {
|
2009-02-11 09:05:00 +00:00
|
|
|
event.stop();
|
|
|
|
|
2009-02-06 22:54:02 +00:00
|
|
|
var note_height = 100;
|
2009-02-07 07:56:36 +00:00
|
|
|
mouse_position = event.mouse().page;
|
2009-02-06 22:01:44 +00:00
|
|
|
|
2009-02-06 22:54:02 +00:00
|
|
|
addElementClass( this.div, "note_div_dragging" );
|
2009-02-06 22:01:44 +00:00
|
|
|
|
|
|
|
// add a blank div to the area where the editor is popping out from. this lets the user easily
|
|
|
|
// see where the editor came from
|
2009-02-09 23:01:44 +00:00
|
|
|
var drop_target = createDOM( "div", { "class": "note_drag_source_area", "id": "note_drag_source_area" } );
|
2009-02-09 22:35:52 +00:00
|
|
|
insertSiblingNodesAfter( this.holder, drop_target );
|
|
|
|
var div_size = getElementDimensions( this.div );
|
|
|
|
div_size.w -= FRAME_BORDER_WIDTH * 2;
|
|
|
|
setElementDimensions( drop_target, div_size );
|
|
|
|
|
2009-02-09 23:01:44 +00:00
|
|
|
// add drop target divs between every note and before the first note
|
2009-02-09 22:35:52 +00:00
|
|
|
var holders = getElementsByTagAndClassName( "table", "note_holder" );
|
2009-02-09 23:01:44 +00:00
|
|
|
if ( holders[ 0 ] != this.holder ) {
|
|
|
|
drop_target = createDOM( "div", { "class": "note_drop_target" } );
|
|
|
|
insertSiblingNodesBefore( holders[ 0 ], drop_target );
|
|
|
|
setElementDimensions( drop_target, { "w": div_size.w } );
|
|
|
|
}
|
|
|
|
|
2009-02-09 22:35:52 +00:00
|
|
|
for ( var i in holders ) {
|
|
|
|
var holder = holders[ i ];
|
|
|
|
i = parseInt( i );
|
|
|
|
var next_holder = ( i + 1 < holders.length ) && holders[ i + 1 ] || null;
|
|
|
|
|
|
|
|
if ( holder == this.holder || next_holder == this.holder ) continue;
|
|
|
|
|
|
|
|
drop_target = createDOM( "div", { "class": "note_drop_target" } );
|
|
|
|
insertSiblingNodesAfter( holder, drop_target );
|
|
|
|
setElementDimensions( drop_target, { "w": div_size.w } );
|
|
|
|
}
|
2009-02-06 22:01:44 +00:00
|
|
|
|
|
|
|
// vertically reposition the editor so that at least some part of it is under the mouse cursor
|
2009-02-07 07:56:36 +00:00
|
|
|
var viewport_position = getViewportPosition();
|
2009-02-06 22:01:44 +00:00
|
|
|
var position = getElementPosition( this.div );
|
2009-02-07 07:56:36 +00:00
|
|
|
|
2009-02-06 22:01:44 +00:00
|
|
|
var new_position = null;
|
|
|
|
setElementDimensions( this.holder, { "w": getElementDimensions( this.holder ).w } );
|
|
|
|
|
|
|
|
// if the click position is above the editor, then move the top of the editor up to it
|
2009-02-07 07:56:36 +00:00
|
|
|
if ( mouse_position.y < position.y )
|
|
|
|
new_position = { "y": mouse_position.y };
|
2009-02-06 22:01:44 +00:00
|
|
|
// if the click position is below the editor, then move the bottom of the editor down to it
|
2009-02-07 07:56:36 +00:00
|
|
|
else if ( mouse_position.y > position.y + note_height )
|
|
|
|
new_position = { "y": mouse_position.y - note_height };
|
|
|
|
else
|
2009-02-06 22:54:02 +00:00
|
|
|
new_position = { "y": getElementPosition( this.holder ).y };
|
2009-02-07 07:56:36 +00:00
|
|
|
|
|
|
|
// save the offset of the mouse click from the top-left of the holder
|
|
|
|
this.drag_click_offset = {
|
|
|
|
"x": mouse_position.x - getElementPosition( this.holder ).x,
|
|
|
|
"y": mouse_position.y - new_position.y
|
|
|
|
};
|
|
|
|
|
|
|
|
new_position.y -= viewport_position.y;
|
|
|
|
setElementPosition( this.holder, new_position );
|
2009-02-06 22:01:44 +00:00
|
|
|
|
|
|
|
addElementClass( this.holder, "note_holder_dragging" );
|
|
|
|
addElementClass( this.note_controls, "invisible" );
|
|
|
|
addElementClass( this.grabber, "note_grabber_focused" );
|
|
|
|
removeElementClass( getElement( "note_shadow_" + this.id ), "undisplayed" );
|
|
|
|
signal( this, "grabber_pressed", event );
|
|
|
|
|
|
|
|
var self = this;
|
2009-02-11 09:05:00 +00:00
|
|
|
this.drag_signal = connect( window.document.body, "onmousemove", function ( event ) { self.drag( event ); } );
|
|
|
|
this.drop_signal = connect( window.document.body, "onmouseup", function ( event ) { self.drop( event ); } );
|
2009-02-06 22:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.drag = function( event ) {
|
2009-02-11 09:05:00 +00:00
|
|
|
event.stop();
|
|
|
|
|
2009-02-10 00:24:45 +00:00
|
|
|
if ( this.drag_target_timer )
|
|
|
|
clearTimeout( this.drag_target_timer );
|
|
|
|
|
2009-02-07 07:56:36 +00:00
|
|
|
var mouse_position = event.mouse().page;
|
2009-02-06 22:01:44 +00:00
|
|
|
|
2009-02-07 07:56:36 +00:00
|
|
|
// move the editor based on the original click's offset
|
|
|
|
var viewport_position = getViewportPosition();
|
|
|
|
setElementPosition( this.holder, {
|
|
|
|
"x": mouse_position.x - this.drag_click_offset.x - viewport_position.x,
|
|
|
|
"y": mouse_position.y - this.drag_click_offset.y - viewport_position.y
|
|
|
|
} );
|
2009-02-06 22:01:44 +00:00
|
|
|
|
2009-02-10 00:24:45 +00:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
function hover_drag_targets() {
|
|
|
|
// if the editor is over any drop targets, expand and color them accordingly
|
|
|
|
var drop_targets = getElementsByTagAndClassName( "div", "note_drop_target" );
|
|
|
|
var div_position = getElementPosition( self.div );
|
|
|
|
var div_size = getElementDimensions( self.div );
|
|
|
|
|
|
|
|
for ( var i in drop_targets ) {
|
|
|
|
var drop_target = drop_targets[ i ];
|
|
|
|
var target_position = getElementPosition( drop_target );
|
|
|
|
var target_size = getElementDimensions( drop_target );
|
2009-02-11 08:46:37 +00:00
|
|
|
if ( WEBKIT ) {
|
|
|
|
target_position.x -= viewport_position.x;
|
|
|
|
target_position.y -= viewport_position.y;
|
|
|
|
}
|
2009-02-10 00:24:45 +00:00
|
|
|
|
|
|
|
// if the div is wholly above or below the drop target, then it's not overlapping it
|
|
|
|
if ( ( div_position.y < target_position.y && div_position.y + div_size.h < target_position.y ) ||
|
|
|
|
( div_position.y > target_position.y + target_size.h && div_position.y + div_size.h > target_position.y + target_size.h ) )
|
|
|
|
removeElementClass( drop_target, "note_drop_target_hover" );
|
|
|
|
else
|
|
|
|
addElementClass( drop_target, "note_drop_target_hover" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// testing drag targets for hovering can be slow, so only do it periodically (a split second after the mouse stops moving)
|
|
|
|
this.drag_target_timer = setTimeout( function () { hover_drag_targets(); }, 10 );
|
|
|
|
|
2009-02-07 07:56:36 +00:00
|
|
|
this.drag_scroll( event.mouse().client );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.drag_scroll = function( mouse_viewport_position ) {
|
|
|
|
if ( this.drag_scroll_timer )
|
|
|
|
clearTimeout( this.drag_scroll_timer );
|
|
|
|
|
|
|
|
// if the mouse is near the top or the bottom of the viewport, scroll the page accordingly. this
|
|
|
|
// allows the user to reach other notes while dragging a note
|
|
|
|
var viewport_size = getViewportDimensions();
|
|
|
|
var scroll_height = 0;
|
|
|
|
var SCROLL_TRIGGER_AREA_HEIGHT = 50;
|
|
|
|
var SCROLL_RATE = 40;
|
|
|
|
|
|
|
|
if ( mouse_viewport_position.y > viewport_size.h - SCROLL_TRIGGER_AREA_HEIGHT )
|
|
|
|
scroll_height = SCROLL_RATE;
|
|
|
|
else if ( mouse_viewport_position.y < SCROLL_TRIGGER_AREA_HEIGHT )
|
|
|
|
scroll_height = -SCROLL_RATE;
|
|
|
|
else {
|
2009-02-06 22:01:44 +00:00
|
|
|
return;
|
2009-02-07 07:56:36 +00:00
|
|
|
}
|
2009-02-06 22:01:44 +00:00
|
|
|
|
2009-02-07 07:56:36 +00:00
|
|
|
var page_top = document.body.scrollTop || window.pageYOffset;
|
|
|
|
window.scrollBy( 0, scroll_height );
|
|
|
|
var new_page_top = document.body.scrollTop || window.pageYOffset;
|
2009-02-06 22:01:44 +00:00
|
|
|
|
2009-02-07 07:56:36 +00:00
|
|
|
// if scrolling didn't work, then we're at the top or the bottom of the page, so bail
|
|
|
|
if ( page_top == new_page_top )
|
|
|
|
return;
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this.drag_scroll_timer = setTimeout( function () { self.drag_scroll( mouse_viewport_position ); }, 50 );
|
2009-02-06 22:01:44 +00:00
|
|
|
}
|
|
|
|
|
2009-02-06 22:54:02 +00:00
|
|
|
Editor.prototype.drop = function( event ) {
|
2009-02-06 22:56:29 +00:00
|
|
|
disconnect( this.drag_signal );
|
|
|
|
disconnect( this.drop_signal );
|
2009-02-07 07:56:36 +00:00
|
|
|
if ( this.drag_scroll_timer )
|
|
|
|
clearTimeout( this.drag_scroll_timer );
|
2009-02-10 00:24:45 +00:00
|
|
|
if ( this.drag_target_timer )
|
|
|
|
clearTimeout( this.drag_target_timer );
|
2009-02-06 22:54:02 +00:00
|
|
|
|
|
|
|
removeElementClass( this.div, "note_div_dragging" );
|
|
|
|
removeElementClass( this.holder, "note_holder_dragging" );
|
|
|
|
removeElementClass( this.note_controls, "invisible" );
|
|
|
|
removeElementClass( this.grabber, "note_grabber_focused" );
|
|
|
|
addElementClass( getElement( "note_shadow_" + this.id ), "undisplayed" );
|
|
|
|
|
|
|
|
this.drag_mouse_position = null;
|
|
|
|
|
2009-02-10 01:03:13 +00:00
|
|
|
// if there is a hovered drop target, move the editor to it
|
|
|
|
var hover_drop_target = getFirstElementByTagAndClassName( "div", "note_drop_target_hover" );
|
|
|
|
if ( hover_drop_target ) {
|
|
|
|
setElementPosition( this.holder, getElementPosition( hover_drop_target ) );
|
|
|
|
swapDOM( hover_drop_target, this.holder );
|
|
|
|
removeElement( "note_drag_source_area" );
|
2009-02-12 22:11:54 +00:00
|
|
|
this.resize();
|
2009-02-10 01:09:37 +00:00
|
|
|
this.highlight();
|
2009-02-10 09:32:08 +00:00
|
|
|
signal( this, "moved", this, this.previous_editor(), this.next_editor() );
|
2009-02-10 01:03:13 +00:00
|
|
|
}
|
|
|
|
|
2009-02-06 22:54:02 +00:00
|
|
|
var drop_targets = getElementsByTagAndClassName( "div", "note_drop_target" );
|
2009-02-09 23:01:44 +00:00
|
|
|
for ( var i in drop_targets )
|
2009-02-06 22:54:02 +00:00
|
|
|
removeElement( drop_targets[ i ] );
|
|
|
|
|
2009-02-10 01:03:13 +00:00
|
|
|
// if there is no hovered drop target, just move the editor back to its original location
|
|
|
|
if ( !hover_drop_target ) {
|
|
|
|
var drag_source_area = getElement( "note_drag_source_area" );
|
|
|
|
setElementPosition( this.holder, getElementPosition( drag_source_area ) );
|
|
|
|
removeElement( drag_source_area );
|
2009-02-12 22:11:54 +00:00
|
|
|
this.resize();
|
2009-02-10 01:18:18 +00:00
|
|
|
this.highlight();
|
2009-02-10 01:03:13 +00:00
|
|
|
}
|
2009-02-06 22:54:02 +00:00
|
|
|
}
|
|
|
|
|
2009-02-10 09:32:08 +00:00
|
|
|
Editor.prototype.previous_editor = function () {
|
|
|
|
var previous_holder = this.holder.previousSibling;
|
|
|
|
while ( previous_holder && previous_holder.nodeValue == "\n" )
|
|
|
|
previous_holder = previous_holder.previousSibling;
|
|
|
|
if ( !previous_holder || !hasElementClass( previous_holder, "note_holder" ) ) return null;
|
|
|
|
var div = getFirstElementByTagAndClassName( "div", "static_note_div", previous_holder );
|
|
|
|
if ( !div || !div.editor ) return null;
|
|
|
|
|
|
|
|
return div.editor;
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.next_editor = function () {
|
|
|
|
var next_holder = this.holder.nextSibling;
|
|
|
|
while ( next_holder && next_holder.nodeValue == "\n" )
|
|
|
|
next_holder = next_holder.nextSibling;
|
|
|
|
if ( !next_holder || !hasElementClass( next_holder, "note_holder" ) ) return null;
|
|
|
|
var div = getFirstElementByTagAndClassName( "div", "static_note_div", next_holder );
|
|
|
|
if ( !div || !div.editor ) return null;
|
|
|
|
|
|
|
|
return div.editor;
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
Editor.prototype.key_pressed = function ( event ) {
|
|
|
|
signal( this, "key_pressed", this, event );
|
|
|
|
|
|
|
|
this.resize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.key_released = function ( event ) {
|
|
|
|
this.resize();
|
|
|
|
|
2007-09-11 00:10:44 +00:00
|
|
|
// if ctrl keys are released, bail
|
2007-07-16 20:22:38 +00:00
|
|
|
var code = event.key().code;
|
2007-11-30 04:00:31 +00:00
|
|
|
var CTRL = 17;
|
|
|
|
if ( event.modifier().ctrl || code == CTRL )
|
2007-07-16 20:22:38 +00:00
|
|
|
return;
|
|
|
|
|
2008-10-06 22:09:13 +00:00
|
|
|
this.cleanup_html( code );
|
2008-01-28 20:45:36 +00:00
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
signal( this, "state_changed", this, false );
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2008-10-06 22:09:13 +00:00
|
|
|
Editor.prototype.cleanup_html = function ( key_code ) {
|
|
|
|
if ( WEBKIT ) {
|
|
|
|
// if enter is pressed while in a title, end title mode, since WebKit doesn't do that for us
|
2008-12-03 22:50:53 +00:00
|
|
|
var ENTER = 13; BACKSPACE = 8;
|
2008-10-06 22:09:13 +00:00
|
|
|
if ( key_code == ENTER && this.state_enabled( "h3" ) )
|
|
|
|
this.exec_command( "h3" );
|
|
|
|
|
2008-12-03 22:50:53 +00:00
|
|
|
// if backspace is pressed, skip WebKit style scrubbing since it can cause problems
|
|
|
|
if ( key_code == BACKSPACE )
|
|
|
|
return null;
|
|
|
|
|
2008-10-06 22:09:13 +00:00
|
|
|
// as of this writing, WebKit doesn't support execCommand( "styleWithCSS" ). for more info, see
|
|
|
|
// https://bugs.webkit.org/show_bug.cgi?id=13490
|
|
|
|
// so to make up for this shortcoming, manually scrub WebKit style spans and other nodes,
|
|
|
|
// replacing them with appropriate tags
|
|
|
|
var style_spans = getElementsByTagAndClassName( "span", null, this.document );
|
|
|
|
var underlines = getElementsByTagAndClassName( "u", null, this.document );
|
|
|
|
var strikethroughs = getElementsByTagAndClassName( "strike", null, this.document );
|
2008-10-06 22:35:38 +00:00
|
|
|
var fonts = getElementsByTagAndClassName( "font", "Apple-style-span", this.document );
|
|
|
|
var nodes = style_spans.concat( underlines ).concat( strikethroughs ).concat( fonts );
|
2008-10-06 22:09:13 +00:00
|
|
|
|
|
|
|
for ( var i in nodes ) {
|
|
|
|
var node = nodes[ i ];
|
|
|
|
if ( !node ) continue;
|
|
|
|
|
|
|
|
var style = node.getAttribute( "style" );
|
|
|
|
|
|
|
|
node.removeAttribute( "class" );
|
2008-10-06 22:35:38 +00:00
|
|
|
if ( style == undefined && node.tagName != "font" && node.tagName != "FONT" )
|
|
|
|
continue;
|
2008-10-06 22:09:13 +00:00
|
|
|
|
|
|
|
var replacement = withDocument( this.document, function () {
|
|
|
|
// font-size is set when ending title mode
|
|
|
|
if ( style.indexOf( "font-size: " ) != -1 )
|
|
|
|
return null;
|
|
|
|
if ( style.indexOf( "text-decoration: none;" ) != -1 || style.length == 0 )
|
|
|
|
return createDOM( "span" );
|
|
|
|
if ( style.indexOf( "font-weight: bold;" ) != -1 )
|
|
|
|
return createDOM( "b" );
|
|
|
|
if ( style.indexOf( "font-style: italic;" ) != -1 )
|
|
|
|
return createDOM( "i" );
|
|
|
|
if ( style.indexOf( "text-decoration: underline;" ) != -1 )
|
|
|
|
return createDOM( "u" );
|
|
|
|
if ( style.indexOf( "text-decoration: line-through;" ) != -1 )
|
|
|
|
return createDOM( "strike" );
|
|
|
|
return null;
|
|
|
|
} );
|
|
|
|
|
|
|
|
if ( replacement ) {
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
var anchor = selection.anchorNode;
|
|
|
|
var offset = selection.anchorOffset;
|
|
|
|
swapDOM( node, replacement );
|
|
|
|
appendChildNodes( replacement, node.childNodes );
|
|
|
|
|
|
|
|
// necessary to prevent the text cursor from disappearing as the node containing it is replaced
|
|
|
|
selection.collapse( anchor, offset );
|
|
|
|
} else {
|
|
|
|
node.removeAttribute( "style" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// the rest only applies to Firefox and other Gecko-based browsers
|
2008-10-03 06:08:56 +00:00
|
|
|
if ( !GECKO )
|
2008-01-28 20:45:36 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// if you're typing the text of an <h3> title and you hit enter, the text cursor will skip a line
|
|
|
|
// and then move back up a line when you start typing again. to prevent this behavior, remove an
|
|
|
|
// extra <br> tag when this situation is detected before the current node: <h3><br>
|
|
|
|
if ( this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) {
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
var range = selection.getRangeAt( 0 );
|
|
|
|
var startOffset = range.startOffset;
|
|
|
|
|
|
|
|
var node = range.startContainer.childNodes[ startOffset ];
|
|
|
|
if ( node && node.previousSibling && node.previousSibling.previousSibling &&
|
|
|
|
node.previousSibling.nodeName == "BR" &&
|
|
|
|
node.previousSibling.previousSibling.nodeName == "H3" ) {
|
|
|
|
removeElement( node.previousSibling );
|
|
|
|
if ( node.nodeName != "BR" )
|
|
|
|
insertSiblingNodesBefore( node, createDOM( "br" ) );
|
|
|
|
else
|
|
|
|
this.resize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-16 23:35:04 +00:00
|
|
|
Editor.prototype.unformat_selection = function () {
|
|
|
|
if ( !this.iframe || !this.document )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// if the selection is collapsed, then there is nothing to unformat
|
|
|
|
if ( this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
var range = selection.getRangeAt( 0 );
|
|
|
|
if ( range.collapsed )
|
|
|
|
return false;
|
|
|
|
} else if ( this.document.selection ) { // browsers such as IE
|
|
|
|
var range = this.document.selection.createRange();
|
|
|
|
var collapsed_range = range.duplicate();
|
|
|
|
collapsed_range.collapse();
|
|
|
|
if ( range.isEqual( collapsed_range ) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// first, remove all standard formatting (<b>, <i>, etc.)
|
|
|
|
this.exec_command( "removeformat" );
|
|
|
|
|
|
|
|
// then, remove all other "formatting" that removeformat doesn't touch (<h1>, <h2>, etc.)
|
|
|
|
var nodes =
|
|
|
|
getElementsByTagAndClassName( "h1", null, this.document ).concat(
|
|
|
|
getElementsByTagAndClassName( "h2", null, this.document ) ).concat(
|
|
|
|
getElementsByTagAndClassName( "h3", null, this.document ) ).concat(
|
|
|
|
getElementsByTagAndClassName( "h4", null, this.document ) ).concat(
|
|
|
|
getElementsByTagAndClassName( "h5", null, this.document ) ).concat(
|
|
|
|
getElementsByTagAndClassName( "h6", null, this.document ) );
|
|
|
|
|
|
|
|
for ( var i in nodes ) {
|
|
|
|
var node = nodes[ i ];
|
|
|
|
if ( !node ) continue;
|
|
|
|
|
|
|
|
// before proceeding, make sure the node is within the selected text
|
|
|
|
if ( selection && selection.containsNode ) { // browsers such as Firefox
|
|
|
|
if ( !selection.containsNode( node, true ) )
|
|
|
|
continue;
|
|
|
|
} else if ( this.document.selection ) {
|
|
|
|
var node_range = this.document.selection.createRange();
|
|
|
|
node_range.moveToElementText( node );
|
|
|
|
if ( !range.inRange( node_range ) )
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var replacement = withDocument( this.document, function () { return createDOM( "span" ); } );
|
|
|
|
swapDOM( node, replacement );
|
|
|
|
appendChildNodes( replacement, node.childNodes );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-01-25 07:20:34 +00:00
|
|
|
Editor.prototype.mouse_released = function ( event ) {
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2008-03-11 00:48:37 +00:00
|
|
|
var self = this;
|
2007-09-11 00:10:44 +00:00
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
function handle_click( event ) {
|
|
|
|
// we only want to deal with left mouse button clicks
|
|
|
|
if ( event.mouse().button.middle || event.mouse().button.right )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// search through the tree of elements containing the clicked target. if a link isn't found, bail
|
|
|
|
var link = event.target()
|
2008-05-19 22:16:25 +00:00
|
|
|
if ( !link ) false;
|
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
while ( link.nodeName != "A" ) {
|
|
|
|
link = link.parentNode;
|
|
|
|
if ( !link )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( !link.href ) return false;
|
|
|
|
|
2009-02-17 00:54:17 +00:00
|
|
|
var query = parse_query( link );
|
|
|
|
var id = query.note_id;
|
|
|
|
|
|
|
|
// special case for links to uploaded files
|
|
|
|
if ( !link.target && /\/files\//.test( link.href ) ) {
|
|
|
|
if ( !/\/files\/new$/.test( link.href ) ) {
|
|
|
|
window.open( link.href );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
// links with targets are considered to be external links pointing outside of this wiki
|
2009-02-17 00:54:17 +00:00
|
|
|
if ( link.target || id == undefined ) {
|
2009-01-26 20:12:51 +00:00
|
|
|
// if this is a read-only editor and the link target is "_top", go to the link's URL directly
|
2009-02-17 00:54:17 +00:00
|
|
|
if ( !self.edit_enabled && ( link.target == "_top" || !link.target ) ) {
|
2009-01-26 20:12:51 +00:00
|
|
|
window.location = link.href;
|
|
|
|
return true;
|
|
|
|
}
|
2008-03-11 00:48:37 +00:00
|
|
|
|
2009-01-26 20:12:51 +00:00
|
|
|
// launch the external link ourselves, assuming that its target is "_new"
|
2008-03-11 00:48:37 +00:00
|
|
|
window.open( link.href );
|
2009-02-17 00:54:17 +00:00
|
|
|
if ( !link.target )
|
|
|
|
link.target = "_top";
|
2008-04-01 21:54:43 +00:00
|
|
|
return true;
|
2008-03-11 00:48:37 +00:00
|
|
|
}
|
2007-07-27 00:32:42 +00:00
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
// load the note corresponding to the clicked link
|
|
|
|
var title = link_title( link, query );
|
2009-01-23 22:55:57 +00:00
|
|
|
signal( self, "load_editor", title, id, null, null, link, self.holder );
|
2008-03-11 00:48:37 +00:00
|
|
|
return true;
|
2008-02-18 20:08:07 +00:00
|
|
|
}
|
|
|
|
|
2008-03-11 00:48:37 +00:00
|
|
|
var link_clicked = handle_click( event );
|
|
|
|
|
2009-01-15 00:37:02 +00:00
|
|
|
if ( this.edit_enabled ) {
|
|
|
|
// if no link was clicked, then make the clicked editor into an iframe
|
|
|
|
if ( !link_clicked && this.div ) {
|
|
|
|
this.init_focus = true;
|
2009-01-15 22:33:54 +00:00
|
|
|
|
2009-01-24 21:57:33 +00:00
|
|
|
this.claim_iframe( null );
|
2009-01-15 00:37:02 +00:00
|
|
|
}
|
2009-01-12 22:49:45 +00:00
|
|
|
|
2009-01-15 00:37:02 +00:00
|
|
|
// in case the cursor has moved, update the state
|
2008-03-11 00:48:37 +00:00
|
|
|
signal( this, "state_changed", this, link_clicked );
|
2009-01-15 00:37:02 +00:00
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2009-01-25 07:20:34 +00:00
|
|
|
Editor.prototype.mouse_clicked = function ( event ) {
|
|
|
|
var target = event.target();
|
|
|
|
if ( !target ) return;
|
|
|
|
|
|
|
|
var tag_name = target.tagName;
|
|
|
|
if ( !tag_name ) return;
|
|
|
|
tag_name = tag_name.toLowerCase();
|
|
|
|
|
|
|
|
// allow clicks on buttons, labels, and input fields
|
|
|
|
if ( tag_name == "button" || tag_name == "label" || tag_name == "input" )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// block all other clicks (e.g. on links, to prevent the browser from handling link clicks itself)
|
|
|
|
event.stop();
|
|
|
|
}
|
|
|
|
|
2008-06-13 22:55:19 +00:00
|
|
|
HOVER_DURATION_MILLISECONDS = 1000;
|
|
|
|
|
|
|
|
Editor.prototype.mouse_hovered = function ( event ) {
|
2009-01-13 00:00:24 +00:00
|
|
|
// ignore mouse hover events for static div notes
|
2009-01-24 23:32:43 +00:00
|
|
|
if ( !this.iframe )
|
2009-01-13 00:00:24 +00:00
|
|
|
return;
|
|
|
|
|
2008-06-13 22:55:19 +00:00
|
|
|
// search through the tree of elements containing the hover target for a link
|
|
|
|
var link = event.target()
|
|
|
|
if ( !link ) false;
|
|
|
|
|
|
|
|
while ( link.nodeName != "A" ) {
|
|
|
|
link = link.parentNode;
|
|
|
|
if ( !link )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( !link || !link.href )
|
|
|
|
link = null;
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
var hover_target = link || event.target();
|
|
|
|
this.hover_target = hover_target;
|
|
|
|
|
|
|
|
if ( this.hover_timer )
|
|
|
|
clearTimeout( this.hover_timer );
|
|
|
|
|
|
|
|
this.hover_timer = setTimeout( function () { self.mouse_hover_timeout( hover_target ) }, HOVER_DURATION_MILLISECONDS );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.mouse_hover_timeout = function ( hover_target ) {
|
|
|
|
// if the mouse is hovering over the same target that it was when the timer started
|
2009-01-25 07:59:23 +00:00
|
|
|
if ( hover_target == this.hover_target && this.iframe )
|
2008-06-13 22:55:19 +00:00
|
|
|
signal( this, "mouse_hovered", hover_target );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.mouse_dragged = function ( event ) {
|
|
|
|
// reset the hover timer to prevent a mouse hover from being registered while the mouse is being dragged
|
|
|
|
if ( this.hover_timer ) {
|
|
|
|
var self = this;
|
|
|
|
clearTimeout( this.hover_timer );
|
|
|
|
this.hover_timer = setTimeout( function () { self.mouse_hover_timeout( self.hover_target ) }, HOVER_DURATION_MILLISECONDS );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
Editor.prototype.scrape_title = function () {
|
2007-07-17 01:21:31 +00:00
|
|
|
// scrape the note title out of the editor
|
2009-01-03 23:52:12 +00:00
|
|
|
var heading = getFirstElementByTagAndClassName( "h3", null, this.document || this.div );
|
2007-10-22 20:52:04 +00:00
|
|
|
if ( heading )
|
|
|
|
var title = scrapeText( heading );
|
|
|
|
else
|
|
|
|
var title = "";
|
2007-07-16 20:22:38 +00:00
|
|
|
|
2007-08-16 01:29:18 +00:00
|
|
|
// issue a signal that the title has changed and save off the new title
|
2008-01-22 20:29:29 +00:00
|
|
|
if ( this.edit_enabled )
|
2007-10-26 20:50:58 +00:00
|
|
|
signal( this, "title_changed", this, this.title, title );
|
2007-07-16 20:22:38 +00:00
|
|
|
this.title = title;
|
|
|
|
}
|
|
|
|
|
2008-10-06 22:48:32 +00:00
|
|
|
Editor.title_placeholder_char = "\u200b";
|
|
|
|
Editor.title_placeholder_pattern = /\u200b/g;
|
|
|
|
Editor.title_placeholder_html = "​​";
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
Editor.prototype.empty = function () {
|
2009-01-22 23:30:17 +00:00
|
|
|
if ( this.iframe && this.document && this.document.body )
|
2009-01-08 20:42:00 +00:00
|
|
|
var node = this.document.body;
|
2009-01-22 23:30:17 +00:00
|
|
|
else if ( this.div )
|
|
|
|
var node = this.div;
|
2009-01-08 20:42:00 +00:00
|
|
|
else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return ( scrapeText( node ).replace( Editor.title_placeholder_pattern, "" ).length == 0 );
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2008-02-01 19:17:10 +00:00
|
|
|
Editor.prototype.insert_link = function ( url ) {
|
2007-07-16 20:22:38 +00:00
|
|
|
// get the current selection, which is the link title
|
|
|
|
if ( this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
|
2008-10-03 06:08:56 +00:00
|
|
|
// if no text is selected, then insert a link with two zero-width spaces as the title. then,
|
|
|
|
// position the text cursor between the two zero-width spaces. yes, this really is necessary.
|
|
|
|
// it ensures that the next character typed in WebKit becomes part of the link title.
|
2007-07-16 20:22:38 +00:00
|
|
|
if ( selection.toString().length == 0 ) {
|
2008-10-03 06:08:56 +00:00
|
|
|
this.insert_html( '<a href="' + url + '" id="new_link">' + Editor.title_placeholder_html + '</a>' );
|
|
|
|
var link = withDocument( this.document, function () { return getElement( "new_link" ); } );
|
|
|
|
link.removeAttribute( "id" );
|
|
|
|
selection.selectAllChildren( link );
|
|
|
|
selection.collapse( link.firstChild, 1 );
|
|
|
|
this.link_started = link;
|
2007-07-16 20:22:38 +00:00
|
|
|
// otherwise, just create a link with the selected text as the link title
|
|
|
|
} else {
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2008-02-01 19:17:10 +00:00
|
|
|
this.exec_command( "createLink", url );
|
2007-11-27 21:28:25 +00:00
|
|
|
return this.find_link_at_cursor();
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
} else if ( this.document.selection ) { // browsers such as IE
|
|
|
|
var range = this.document.selection.createRange();
|
|
|
|
|
|
|
|
// if no text is selected, then insert a link with a placeholder space as the link title, and
|
|
|
|
// then select it
|
|
|
|
if ( range.text.length == 0 ) {
|
|
|
|
range.text = " ";
|
|
|
|
range.moveStart( "character", -1 );
|
|
|
|
range.select();
|
2008-02-01 19:17:10 +00:00
|
|
|
this.exec_command( "createLink", url );
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = this.find_link_at_cursor();
|
2007-08-14 04:13:49 +00:00
|
|
|
} else {
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2008-02-01 19:17:10 +00:00
|
|
|
this.exec_command( "createLink", url );
|
2007-11-27 21:28:25 +00:00
|
|
|
return this.find_link_at_cursor();
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-01 19:17:10 +00:00
|
|
|
Editor.prototype.start_link = function () {
|
|
|
|
return this.insert_link( "/notebooks/" + this.notebook_id + "?note_id=new" );
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.start_file_link = function () {
|
|
|
|
return this.insert_link( "/files/new" );
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
Editor.prototype.end_link = function () {
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
var link = this.find_link_at_cursor();
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
if ( this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
|
|
|
this.exec_command( "unlink" );
|
2008-10-03 06:08:56 +00:00
|
|
|
|
|
|
|
// necessary to actually end a link in WebKit. the side-effect is that the cursor jumps to the
|
|
|
|
// end of the link if it's not already there
|
|
|
|
if ( link && WEBKIT ) {
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
2008-12-03 21:10:18 +00:00
|
|
|
var sentinel = this.document.createTextNode( Editor.title_placeholder_char );
|
|
|
|
insertSiblingNodesAfter( link, sentinel );
|
|
|
|
selection.collapse( sentinel, 1 );
|
2008-10-03 06:08:56 +00:00
|
|
|
}
|
2007-07-16 20:22:38 +00:00
|
|
|
} else if ( this.document.selection ) { // browsers such as IE
|
|
|
|
// if some text is already selected, unlink it and bail
|
|
|
|
var range = this.document.selection.createRange();
|
|
|
|
if ( range.text.length > 0 ) {
|
|
|
|
this.exec_command( "unlink" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// since execCommand() with "unlink" removes the entire link instead of just ending it, fake it
|
|
|
|
// by appending a temporary span, selecting it, and then immediately removing it
|
|
|
|
var span = this.document.createElement( "span" );
|
|
|
|
span.innerHTML = " ";
|
2007-07-18 21:51:59 +00:00
|
|
|
range.parentElement().appendChild( span );
|
2007-07-16 20:22:38 +00:00
|
|
|
range.moveToElementText( span );
|
|
|
|
range.select();
|
|
|
|
range.pasteHTML( "" );
|
|
|
|
}
|
2007-08-14 04:13:49 +00:00
|
|
|
|
2007-08-30 20:17:10 +00:00
|
|
|
return link;
|
2007-08-14 04:13:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.find_link_at_cursor = function () {
|
2009-01-03 23:52:12 +00:00
|
|
|
if ( this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.getSelection ) { // browsers such as Firefox
|
2007-08-14 04:13:49 +00:00
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
|
|
|
var link = selection.anchorNode;
|
2008-05-19 22:16:25 +00:00
|
|
|
if ( !link ) return null;
|
2007-08-14 04:13:49 +00:00
|
|
|
|
|
|
|
while ( link.nodeName != "A" ) {
|
|
|
|
link = link.parentNode;
|
|
|
|
if ( !link )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-09-11 00:10:44 +00:00
|
|
|
if ( link != this.link_started )
|
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
if ( link ) return link;
|
|
|
|
|
|
|
|
// well, that didn't work, so try the selection's focus node instead
|
|
|
|
link = selection.focusNode;
|
|
|
|
|
|
|
|
while ( link.nodeName != "A" ) {
|
|
|
|
link = link.parentNode;
|
2007-09-11 00:10:44 +00:00
|
|
|
if ( !link ) {
|
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
return null;
|
2007-09-11 00:10:44 +00:00
|
|
|
}
|
2007-08-14 04:13:49 +00:00
|
|
|
}
|
|
|
|
|
2007-09-11 00:10:44 +00:00
|
|
|
if ( link != this.link_started )
|
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
return link;
|
2009-01-03 23:52:12 +00:00
|
|
|
} else if ( this.document && this.document.selection ) { // browsers such as IE
|
2007-08-14 04:13:49 +00:00
|
|
|
var range = this.document.selection.createRange();
|
|
|
|
var link = range.parentElement();
|
|
|
|
|
|
|
|
while ( link.nodeName != "A" ) {
|
|
|
|
link = link.parentNode;
|
2007-09-11 00:10:44 +00:00
|
|
|
if ( !link ) {
|
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
return null;
|
2007-09-11 00:10:44 +00:00
|
|
|
}
|
2007-08-14 04:13:49 +00:00
|
|
|
}
|
|
|
|
|
2007-09-11 00:10:44 +00:00
|
|
|
if ( link != this.link_started )
|
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
2007-09-11 00:10:44 +00:00
|
|
|
this.link_started = null;
|
2007-08-14 04:13:49 +00:00
|
|
|
return null;
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2009-01-22 22:23:07 +00:00
|
|
|
Editor.prototype.focus = function ( suppress_signal ) {
|
2009-01-12 22:19:32 +00:00
|
|
|
if ( this.div && this.edit_enabled )
|
2009-01-21 21:33:11 +00:00
|
|
|
this.claim_iframe();
|
2009-01-10 00:43:33 +00:00
|
|
|
|
2009-01-12 22:19:32 +00:00
|
|
|
if ( this.iframe ) {
|
2009-01-23 22:36:12 +00:00
|
|
|
addElementClass( this.iframe, "focused_note_frame" );
|
|
|
|
|
2009-01-12 22:19:32 +00:00
|
|
|
if ( OPERA )
|
|
|
|
this.iframe.focus();
|
|
|
|
else
|
|
|
|
this.iframe.contentWindow.focus();
|
|
|
|
}
|
|
|
|
|
2009-01-22 22:23:07 +00:00
|
|
|
if ( !suppress_signal )
|
|
|
|
signal( this, "focused", this );
|
2009-01-12 22:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.blur = function () {
|
2009-01-23 06:33:32 +00:00
|
|
|
this.scrape_title();
|
|
|
|
|
|
|
|
removeElementClass( this.iframe || this.div, "focused_note_frame" );
|
2009-02-12 11:15:39 +00:00
|
|
|
if ( this.grabber )
|
|
|
|
removeElementClass( this.grabber, "note_grabber_focused" );
|
2009-01-23 06:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.release_iframe = function () {
|
2009-01-23 00:54:56 +00:00
|
|
|
if ( !this.iframe )
|
|
|
|
return;
|
|
|
|
|
2009-01-23 06:33:32 +00:00
|
|
|
var contents = this.contents();
|
2009-01-23 00:54:56 +00:00
|
|
|
|
|
|
|
disconnectAll( this.iframe.contentWindow );
|
|
|
|
disconnectAll( this.iframe );
|
|
|
|
disconnectAll( this.document.body );
|
|
|
|
disconnectAll( this.document );
|
|
|
|
this.iframe.editor = null;
|
|
|
|
this.document = null;
|
2009-01-12 22:19:32 +00:00
|
|
|
|
2009-01-23 06:33:32 +00:00
|
|
|
var static_contents = getFirstElementByTagAndClassName( "span", "static_note_contents", this.div );
|
2009-02-12 11:15:39 +00:00
|
|
|
if ( static_contents )
|
|
|
|
static_contents.innerHTML = contents;
|
2009-01-23 06:33:32 +00:00
|
|
|
|
2009-02-12 11:15:39 +00:00
|
|
|
removeElementClass( this.div, "invisible" );
|
2009-01-23 00:54:56 +00:00
|
|
|
addElementClass( this.iframe, "invisible" );
|
2009-01-23 22:49:03 +00:00
|
|
|
setElementDimensions( this.iframe, { "h": 0 } );
|
2009-01-23 00:54:56 +00:00
|
|
|
this.iframe = null;
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2009-01-08 20:42:00 +00:00
|
|
|
Editor.prototype.contents = function () {
|
2009-01-23 06:33:32 +00:00
|
|
|
if ( this.iframe && this.document && this.document.body )
|
|
|
|
return this.document.body.innerHTML;
|
|
|
|
|
2009-01-07 20:39:48 +00:00
|
|
|
if ( this.div ) {
|
|
|
|
var static_contents = getFirstElementByTagAndClassName( "span", "static_note_contents", this.div );
|
|
|
|
if ( static_contents )
|
|
|
|
return static_contents.innerHTML;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.initial_text || "";
|
2007-07-16 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-30 01:00:03 +00:00
|
|
|
// return true if the given state_name is currently enabled, optionally using a given list of node
|
|
|
|
// names
|
|
|
|
Editor.prototype.state_enabled = function ( state_name, node_names ) {
|
2008-01-22 20:29:29 +00:00
|
|
|
if ( !this.edit_enabled )
|
2007-11-30 01:00:03 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
state_name = state_name.toLowerCase();
|
|
|
|
if ( !node_names )
|
|
|
|
node_names = this.current_node_names();
|
|
|
|
|
|
|
|
for ( var i in node_names ) {
|
|
|
|
var node_name = node_names[ i ];
|
|
|
|
if ( node_name == state_name )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a list of names for all the nodes containing the cursor
|
|
|
|
Editor.prototype.current_node_names = function () {
|
|
|
|
var node_names = new Array();
|
|
|
|
|
2009-01-07 00:20:04 +00:00
|
|
|
if ( !this.edit_enabled || !this.iframe || !this.document )
|
2007-11-30 01:00:03 +00:00
|
|
|
return node_names;
|
|
|
|
|
|
|
|
// to determine whether the specified state is enabled, see whether the current selection is
|
|
|
|
// contained (directly or indirectly) by a node of the appropriate type (e.g. "h3", "a", etc.)
|
|
|
|
var node;
|
|
|
|
if ( window.getSelection ) { // browsers such as Firefox
|
|
|
|
var selection = this.iframe.contentWindow.getSelection();
|
2008-10-06 22:09:13 +00:00
|
|
|
if ( selection.rangeCount > 0 )
|
|
|
|
var range = selection.getRangeAt( 0 );
|
|
|
|
else
|
|
|
|
var range = this.document.createRange();
|
2007-11-30 01:00:03 +00:00
|
|
|
node = range.endContainer;
|
|
|
|
} else if ( this.document.selection ) { // browsers such as IE
|
|
|
|
var range = this.document.selection.createRange();
|
|
|
|
node = range.parentElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( node ) {
|
|
|
|
var name = node.nodeName.toLowerCase();
|
|
|
|
|
2007-11-30 01:45:07 +00:00
|
|
|
if ( name == "strong" ) name = "b";
|
|
|
|
if ( name == "em" ) name = "i";
|
|
|
|
|
2007-11-30 01:00:03 +00:00
|
|
|
if ( name != "a" || node.href )
|
|
|
|
node_names.push( name );
|
|
|
|
|
|
|
|
node = node.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return node_names;
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:22:38 +00:00
|
|
|
Editor.prototype.shutdown = function( event ) {
|
2007-08-16 01:29:18 +00:00
|
|
|
signal( this, "title_changed", this, this.title, null );
|
2007-09-05 18:49:28 +00:00
|
|
|
this.closed = true;
|
2007-07-17 01:21:31 +00:00
|
|
|
var note_controls = this.note_controls;
|
2009-01-09 21:10:33 +00:00
|
|
|
|
|
|
|
if ( this.iframe ) {
|
|
|
|
var iframe = this.iframe;
|
2009-01-16 03:14:07 +00:00
|
|
|
// keeping a reference to the iframe allows removeElement( editor_node ) below to work in IE 6
|
|
|
|
if ( !MSIE6 )
|
|
|
|
this.iframe = null;
|
2009-01-09 21:10:33 +00:00
|
|
|
disconnectAll( this );
|
|
|
|
disconnectAll( this.delete_button );
|
|
|
|
disconnectAll( this.changes_button );
|
2009-03-03 03:39:02 +00:00
|
|
|
disconnectAll( this.tools_button );
|
2009-01-09 21:10:33 +00:00
|
|
|
disconnectAll( this.hide_button );
|
2009-02-06 22:01:44 +00:00
|
|
|
disconnectAll( this.grabber );
|
2009-01-09 21:10:33 +00:00
|
|
|
disconnectAll( iframe );
|
|
|
|
}
|
2007-08-21 21:25:59 +00:00
|
|
|
|
|
|
|
if ( this.document ) {
|
|
|
|
disconnectAll( this.document.body );
|
|
|
|
disconnectAll( this.document );
|
|
|
|
}
|
|
|
|
|
2009-01-21 21:33:11 +00:00
|
|
|
disconnectAll( this.div );
|
2009-01-23 22:55:57 +00:00
|
|
|
var holder = this.holder;
|
2009-01-13 00:37:03 +00:00
|
|
|
|
2009-02-05 07:45:19 +00:00
|
|
|
blindUp( this.div, options = { "duration": 0.25, afterFinish: function () {
|
2007-07-16 20:22:38 +00:00
|
|
|
try {
|
2009-01-23 00:54:56 +00:00
|
|
|
removeElement( holder );
|
2009-01-22 20:15:32 +00:00
|
|
|
} catch ( e ) { }
|
|
|
|
} } );
|
2009-01-22 21:19:40 +00:00
|
|
|
|
|
|
|
if ( !iframe )
|
|
|
|
return;
|
|
|
|
|
2009-01-22 22:53:46 +00:00
|
|
|
// overriding afterFinishInternal for the iframe prevents it from being set to "display: none",
|
|
|
|
// which would break subsequent getSelection() calls
|
2009-01-22 21:30:00 +00:00
|
|
|
iframe.editor = null;
|
2009-01-22 22:53:46 +00:00
|
|
|
blindUp( iframe, options = { "duration": 0.25, afterFinishInternal: function () {
|
2009-01-22 20:15:32 +00:00
|
|
|
try {
|
2009-01-22 22:53:46 +00:00
|
|
|
addElementClass( iframe, "invisible" );
|
2007-07-16 20:22:38 +00:00
|
|
|
} catch ( e ) { }
|
|
|
|
} } );
|
|
|
|
}
|
2007-08-14 04:13:49 +00:00
|
|
|
|
2007-11-27 21:28:25 +00:00
|
|
|
Editor.prototype.summarize = function () {
|
2008-07-07 22:38:16 +00:00
|
|
|
if ( this.document && this.document.body )
|
|
|
|
return summarize_html( scrapeText( this.document.body ), this.title );
|
|
|
|
|
2009-01-24 23:32:43 +00:00
|
|
|
if ( this.div )
|
|
|
|
return summarize_html( scrapeText( this.div ), this.title );
|
|
|
|
|
2008-07-07 22:38:16 +00:00
|
|
|
return "";
|
2008-06-30 03:32:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function summarize_html( html, title ) {
|
|
|
|
var span = createDOM( "span", {} );
|
|
|
|
span.innerHTML = html;
|
|
|
|
var summary = strip( scrapeText( span ) );
|
2007-11-27 21:28:25 +00:00
|
|
|
|
2008-06-30 03:32:13 +00:00
|
|
|
// remove the title (if any) from the summary text
|
|
|
|
if ( title && summary.indexOf( title ) == 0 )
|
|
|
|
summary = summary.substr( title.length );
|
2007-11-27 21:28:25 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-05-18 06:17:37 +00:00
|
|
|
// return the given html in a normal form. this makes html string comparisons more accurate
|
2008-06-30 03:32:13 +00:00
|
|
|
function normalize_html( html ) {
|
2008-05-18 08:22:00 +00:00
|
|
|
if ( !html ) return html;
|
|
|
|
|
2008-05-18 08:07:15 +00:00
|
|
|
// remove any "pulldown" attributes, which get added in IE whenever link.pulldown is set
|
|
|
|
var normal_html = html.replace( /\s+pulldown="null"/g, "" );
|
|
|
|
|
|
|
|
// convert absolute URLs to the server into relative URLs. accomplish this by removing, for
|
|
|
|
// instance, "https://luminotes.com" from any URLs. this is necessary becuase IE insists on
|
|
|
|
// converting relative link URLs to absolute URLs
|
|
|
|
var base_url = window.location.protocol + "//" + window.location.host;
|
|
|
|
normal_html = normal_html.replace( '="' + base_url + '/', '="/' );
|
2008-05-18 06:17:37 +00:00
|
|
|
|
2008-05-18 08:07:15 +00:00
|
|
|
return normal_html;
|
2008-05-18 06:17:37 +00:00
|
|
|
}
|
|
|
|
|
2008-03-05 02:56:58 +00:00
|
|
|
Editor.prototype.dirty = function () {
|
2008-05-18 06:17:37 +00:00
|
|
|
var original_html = normalize_html( this.initial_text )
|
2009-01-07 20:57:51 +00:00
|
|
|
var current_html = normalize_html( this.contents() );
|
2008-05-17 23:16:27 +00:00
|
|
|
|
2009-01-07 20:57:51 +00:00
|
|
|
if ( current_html == "" || current_html == original_html )
|
2008-03-05 02:56:58 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Editor.prototype.mark_clean = function () {
|
2009-01-07 20:57:51 +00:00
|
|
|
this.initial_text = this.contents();
|
2008-03-05 02:56:58 +00:00
|
|
|
}
|
|
|
|
|
2008-03-07 23:14:39 +00:00
|
|
|
Editor.prototype.mark_dirty = function () {
|
|
|
|
this.initial_text = null;
|
|
|
|
}
|
|
|
|
|
2007-08-14 04:13:49 +00:00
|
|
|
// convenience function for parsing a link that has an href URL containing a query string
|
|
|
|
function parse_query( link ) {
|
2007-08-30 20:17:10 +00:00
|
|
|
if ( !link || !link.href )
|
2007-08-15 20:46:37 +00:00
|
|
|
return new Array();
|
|
|
|
|
2007-08-14 04:13:49 +00:00
|
|
|
return parseQueryString( link.href.split( "?" ).pop() );
|
|
|
|
}
|
|
|
|
|
2007-08-15 00:18:30 +00:00
|
|
|
// convenience function for getting a link's title (stripped of whitespace), either from a query
|
2007-10-17 21:22:21 +00:00
|
|
|
// argument in the href, from the actual link title, or from the link's href
|
2007-08-15 00:18:30 +00:00
|
|
|
function link_title( link, query ) {
|
2007-10-22 23:17:56 +00:00
|
|
|
if ( link && link.target )
|
2007-10-17 21:22:21 +00:00
|
|
|
return link.href;
|
|
|
|
|
2007-08-15 00:18:30 +00:00
|
|
|
if ( !query )
|
|
|
|
query = parse_query( link );
|
|
|
|
|
|
|
|
var link_title = strip( query.title || scrapeText( link ) );
|
|
|
|
|
|
|
|
// work around an IE quirk in which link titles are sometimes 0xa0
|
|
|
|
if ( link_title.charCodeAt( 0 ) == 160 )
|
|
|
|
return "";
|
|
|
|
|
2008-10-03 06:08:56 +00:00
|
|
|
return link_title.replace( Editor.title_placeholder_pattern, "" );
|
2007-08-15 00:18:30 +00:00
|
|
|
}
|
2009-01-07 22:15:22 +00:00
|
|
|
|
|
|
|
function normalize_title( title ) {
|
|
|
|
return title.replace( Editor.title_placeholder_pattern, "" ) || "untitled note";
|
|
|
|
}
|
2009-01-12 22:19:32 +00:00
|
|
|
|
|
|
|
function editor_by_id( note_id, revision ) {
|
2009-02-17 00:30:48 +00:00
|
|
|
if ( revision ) {
|
2009-01-12 22:19:32 +00:00
|
|
|
var iframe = getElement( "note_" + note_id + " " + revision );
|
2009-02-17 00:30:48 +00:00
|
|
|
if ( !iframe && getElement( "note_" + note_id ) )
|
|
|
|
return null;
|
|
|
|
} else {
|
2009-01-12 22:19:32 +00:00
|
|
|
var iframe = getElement( "note_" + note_id );
|
2009-02-17 00:30:48 +00:00
|
|
|
}
|
2009-01-12 22:19:32 +00:00
|
|
|
|
|
|
|
if ( iframe )
|
|
|
|
return iframe.editor;
|
|
|
|
|
|
|
|
var div = getElement( "static_note_" + note_id );
|
|
|
|
|
2009-01-13 20:20:36 +00:00
|
|
|
if ( div ) {
|
|
|
|
if ( revision && div.editor && div.editor.revision != revision )
|
|
|
|
return null;
|
|
|
|
|
2009-01-12 22:19:32 +00:00
|
|
|
return div.editor;
|
2009-01-13 20:20:36 +00:00
|
|
|
}
|
2009-01-12 22:19:32 +00:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|