r94326 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r94325‎ | r94326 | r94327 >
Date:06:52, 12 August 2011
Author:inez
Status:deferred
Tags:
Comment:
Rough implementation of new handling for lists - old implementation kept for now in oldListBlock (a lot of functionality and comments to be moved)
Modified paths:
  • /trunk/parsers/wikidom/lib/es/es.ListBlock.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.ListBlockItem.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.ListBlockList.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/oldListBlock (added) (history)
  • /trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlock.js (added) (history)
  • /trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockItem.js (added) (history)
  • /trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockList.js (added) (history)

Diff [purge]

Index: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockList.js
@@ -0,0 +1,115 @@
 2+/**
 3+ * Number or bullet list.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.EventEmitter}
 8+ * @extends {es.Container}
 9+ * @param style {String} Style of list, either "number" or "bullet"
 10+ * @param items {Array} List of es.ListBlockItem objects to initialize list with
 11+ * @property style {String} Style of list
 12+ * @property items {Array} List of es.ListBlockItem objects
 13+ */
 14+es.ListBlockList = function( style, items ) {
 15+ es.EventEmitter.call( this );
 16+ es.Container.call( this, 'list', 'items', items, 'ul' );
 17+ this.style = style || 'bullet';
 18+ if ( this.style == 'number' ) {
 19+ this.$.css('list-style', 'decimal');
 20+ }
 21+};
 22+
 23+/* Static Methods */
 24+
 25+/**
 26+ * Creates an EditSurface list object from a WikiDom list object.
 27+ *
 28+ * @static
 29+ * @method
 30+ * @param wikidomListItem {Object} WikiDom list item
 31+ * @returns {es.ListBlockItem} EditSurface list block item
 32+ */
 33+es.ListBlockList.newFromWikiDomList = function( wikidomList ) {
 34+ var items = [];
 35+ for ( var i = 0; i < wikidomList.items.length; i++ ) {
 36+ items.push( es.ListBlockItem.newFromWikiDomListItem( wikidomList.items[i] ) );
 37+ }
 38+ return new es.ListBlockList( wikidomList.style, items );
 39+};
 40+
 41+/* Methods */
 42+
 43+/**
 44+ * Gets the length of content in both the line and sub-lists.
 45+ *
 46+ * @method
 47+ * @returns {Integer} Length of content
 48+ */
 49+es.ListBlockList.prototype.getLength = function() {
 50+ var length = 0;
 51+ for ( var i = 0; i < this.items.length; i++ ) {
 52+ length += this.items[i].getLength();
 53+ }
 54+ return length;
 55+};
 56+
 57+/**
 58+ * Gets a location from an offset.
 59+ *
 60+ * @method
 61+ * @param offset {Integer} Offset to get location for
 62+ * @returns {Object} Location object with item and offset properties, where offset is local
 63+ * to item.
 64+ */
 65+es.ListBlockList.prototype.getLocationFromOffset = function( offset ) {
 66+ var itemOffset = 0,
 67+ itemLength;
 68+ for ( var i = 0; i < this.items.length; i++ ) {
 69+ itemLength = this.items[i].getLength();
 70+ if ( offset >= itemOffset && offset < itemOffset + itemLength ) {
 71+ return this.items[i].getLocationFromOffset( offset - itemOffset );
 72+ }
 73+ itemOffset += itemLength;
 74+ }
 75+};
 76+
 77+/**
 78+ * Gets an offset within the list from a position.
 79+ *
 80+ * @method
 81+ * @param position {es.Position} Position to translate
 82+ * @returns {Integer} Offset nearest position
 83+ * @returns {Null} If offset could not be found
 84+ */
 85+es.ListBlockList.prototype.getOffsetFromPosition = function( position ) {
 86+ var itemOffset = null,
 87+ globalOffset = null;
 88+
 89+ for ( var i = 0; i < this.items.length; i++ ) {
 90+ itemOffset = this.items[i].getOffsetFromPosition( position );
 91+
 92+ if ( itemOffset !== null ) {
 93+ return globalOffset + itemOffset;
 94+ } else {
 95+ globalOffset += this.items[i].getLength();
 96+ }
 97+ }
 98+};
 99+
 100+/**
 101+ * Renders items.
 102+ *
 103+ * @method
 104+ * @param offset {Integer} Offset to render from if possible
 105+ */
 106+es.ListBlockList.prototype.renderContent = function( offset ) {
 107+ // TODO: Abstract offset and use it when rendering
 108+ for ( var i = 0; i < this.items.length; i++ ) {
 109+ this.items[i].renderContent();
 110+ }
 111+};
 112+
 113+/* Inheritance */
 114+
 115+es.extend( es.ListBlockList, es.EventEmitter );
 116+es.extend( es.ListBlockList, es.Container );
Property changes on: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockList.js
___________________________________________________________________
Added: svn:eol-style
1117 + native
Index: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockItem.js
@@ -0,0 +1,190 @@
 2+/**
 3+ * List item.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.EventEmitter}
 8+ * @extends {es.Container}
 9+ * @param content {es.Content} Item content
 10+ * @param lists {Array} List of item sub-lists
 11+ * @property content {es.Content} Item content
 12+ * @property lists {Array} List of item sub-lists
 13+ * @property $line {jQuery} Line element
 14+ * @property $content {jQuery} Content element
 15+ * @property flow {es.Flow} Text flow object for content
 16+ */
 17+es.ListBlockItem = function( content, lists ) {
 18+ es.EventEmitter.call( this );
 19+ es.Container.call( this, 'item', 'lists', lists, 'li' );
 20+
 21+ this.content = content || new es.Content();
 22+ this.$content = $( '<div class="editSurface-list-content"></div>' );
 23+ this.$.prepend( this.$content );
 24+ this.flow = new es.Flow( this.$content, this.content );
 25+
 26+ /*
 27+ this.content = content || new es.Content();
 28+ this.$line = $( '<div class="editSurface-list-line"></div>' );
 29+ this.$content = $( '<div class="editSurface-list-content"></div>' );
 30+ this.$.prepend( this.$line.append( this.$content ) );
 31+ this.flow = new es.Flow( this.$content, this.content );
 32+ */
 33+
 34+ var listBlockItem = this;
 35+ this.flow.on( 'render', function() {
 36+ listBlockItem.emit( 'update' );
 37+ } );
 38+}
 39+
 40+/* Static Methods */
 41+
 42+/**
 43+ * Creates an EditSurface list item object from a WikiDom list item object.
 44+ *
 45+ * @static
 46+ * @method
 47+ * @param wikidomListItem {Object} WikiDom list item
 48+ * @returns {es.ListBlockItem} EditSurface list block item
 49+ */
 50+es.ListBlockItem.newFromWikiDomListItem = function( wikidomListItem ) {
 51+ // Convert items to es.ListBlockItem objects
 52+ var lists = [];
 53+ if ( wikidomListItem.lists ) {
 54+ for ( var i = 0; i < wikidomListItem.lists.length; i++ ) {
 55+ lists.push( es.ListBlockList.newFromWikiDomList( wikidomListItem.lists[i] ) );
 56+ }
 57+ }
 58+ return new es.ListBlockItem( es.Content.newFromWikiDomLine( wikidomListItem.line ), lists );
 59+};
 60+
 61+/* Methods */
 62+
 63+/**
 64+ * Gets the index of the item within it's list.
 65+ *
 66+ * TODO: Move to es.Container
 67+ *
 68+ * @method
 69+ * @returns {Integer} Index of item
 70+ */
 71+es.ListBlockItem.prototype.getIndex = function() {
 72+ return this.list._list.indexOf( this );
 73+};
 74+
 75+/**
 76+ * Gets the length of content in both the line and sub-lists.
 77+ *
 78+ * @method
 79+ * @returns {Integer} Length of content
 80+ */
 81+es.ListBlockItem.prototype.getLength = function() {
 82+ var length = this.content.getLength() + 1;
 83+ for ( var i = 0; i < this.lists.length; i++ ) {
 84+ length += this.lists[i].getLength();
 85+ }
 86+ return length;
 87+};
 88+
 89+/**
 90+ * Gets a location from an offset.
 91+ *
 92+ * @method
 93+ * @param offset {Integer} Offset to get location for
 94+ * @returns {Object} Location object with item and offset properties, where offset is local
 95+ * to item.
 96+ */
 97+es.ListBlockItem.prototype.getLocationFromOffset = function( offset ) {
 98+ var contentLength = this.content.getLength() + 1;
 99+ if ( offset < contentLength ) {
 100+ return {
 101+ 'item': this,
 102+ 'offset': offset
 103+ };
 104+ }
 105+ offset -= contentLength;
 106+ var listOffset = 0,
 107+ listLength;
 108+ for ( var i = 0; i < this.lists.length; i++ ) {
 109+ listLength = this.lists[i].getLength();
 110+ if ( offset >= listOffset && offset < listOffset + listLength ) {
 111+ return this.lists[i].getLocationFromOffset( offset - listOffset );
 112+ }
 113+ listOffset += listLength;
 114+ }
 115+};
 116+
 117+/**
 118+ * Gets an offset within the item from a position.
 119+ *
 120+ * @method
 121+ * @param position {es.Position} Position to translate
 122+ * @returns {Integer} Offset nearest position
 123+ * @returns {Null} If offset could not be found
 124+ */
 125+es.ListBlockItem.prototype.getOffsetFromPosition = function( position ) {
 126+ var itemOffset = this.$.offset(),
 127+ itemHeight = this.$.height(),
 128+ offset = null,
 129+ globalOffset = null;
 130+
 131+ if ( position.top >= itemOffset.top && position.top < itemOffset.top + itemHeight ) {
 132+ if ( position.top < itemOffset.top + this.$content.height() ) {
 133+ position.top -= itemOffset.top;
 134+ position.left -= itemOffset.left;
 135+ return globalOffset + this.flow.getOffset( position );
 136+ }
 137+
 138+ for ( var i = 0; i < this.lists.length; i++ ) {
 139+ offset = this.lists[i].getOffsetFromPosition( position );
 140+ if ( offset != null ) {
 141+ return globalOffset + offset + this.content.getLength() + 1;
 142+ } else {
 143+ globalOffset += this.lists[i].getLength();
 144+ }
 145+ }
 146+ }
 147+ return null;
 148+};
 149+
 150+/**
 151+ * Gets a depth level for the item in list.
 152+ * Example:
 153+ *
 154+ * # level 0
 155+ * ## level 1
 156+ * # level 0
 157+ * ### level 2
 158+ *
 159+ * @method
 160+ * @returns {Integer} Depth level
 161+ */
 162+es.ListBlockItem.prototype.getLevel = function( position ) {
 163+ var start = this.list.item,
 164+ level = 0;
 165+
 166+ while ( start ) {
 167+ start = start.list.item;
 168+ level++;
 169+ }
 170+
 171+ return level;
 172+};
 173+
 174+/**
 175+ * Renders content and sub-lists.
 176+ *
 177+ * @method
 178+ * @param offset {Integer} Offset to render from if possible
 179+ */
 180+es.ListBlockItem.prototype.renderContent = function( offset ) {
 181+ // TODO: Abstract offset and use it when rendering
 182+ this.flow.render();
 183+ for ( var i = 0; i < this.lists.length; i++ ) {
 184+ this.lists[i].renderContent();
 185+ }
 186+};
 187+
 188+/* Inheritance */
 189+
 190+es.extend( es.ListBlockItem, es.EventEmitter );
 191+es.extend( es.ListBlockItem, es.Container );
Property changes on: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlockItem.js
___________________________________________________________________
Added: svn:eol-style
1192 + native
Index: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlock.js
@@ -0,0 +1,434 @@
 2+/**
 3+ * Creates a list block.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.Block}
 8+ * @param list {es.ListBlockList} Root list to initialize with
 9+ * @property list {es.ListBlockList}
 10+ * @property $ {jQuery}
 11+ */
 12+es.ListBlock = function( list ) {
 13+ es.Block.call( this );
 14+ this.list = list || new es.ListBlockList();
 15+ this.$ = this.list.$
 16+ .addClass( 'editSurface-block' )
 17+ .data( 'block', this );
 18+ var listBlock = this;
 19+ this.list.on( 'update', function() {
 20+ listBlock.emit( 'update' );
 21+ } );
 22+};
 23+
 24+/* Static Methods */
 25+
 26+/**
 27+ * Creates a new list block object from WikiDom data.
 28+ *
 29+ * @static
 30+ * @method
 31+ * @param wikidomParagraphBlock {Object} WikiDom data to convert from
 32+ * @returns {es.ListBlock} EditSurface list block
 33+ */
 34+es.ListBlock.newFromWikiDomListBlock = function( wikidomListBlock ) {
 35+ if ( wikidomListBlock.type !== 'list' ) {
 36+ throw 'Invalid block type error. List block expected to be of type "list".';
 37+ }
 38+ return new es.ListBlock( es.ListBlockList.newFromWikiDomList( wikidomListBlock ) );
 39+};
 40+
 41+/* Methods */
 42+
 43+/**
 44+ * Gets the length of all block content.
 45+ *
 46+ * @method
 47+ * @returns {Integer} Length of content
 48+ */
 49+es.ListBlock.prototype.getLength = function() {
 50+ // Compensate for n+1 virtual position on the last item's content
 51+ return this.list.getLength() - 1;
 52+};
 53+
 54+/**
 55+ * Inserts content into a block at an offset.
 56+ *
 57+ * @method
 58+ * @param offset {Integer} Position to insert content at
 59+ * @param content {Object} Content to insert
 60+ */
 61+es.ListBlock.prototype.insertContent = function( offset, content ) {
 62+ var location = this.list.getLocationFromOffset( offset );
 63+ location.item.flow.content.insert( location.offset, content );
 64+};
 65+
 66+/**
 67+ * Deletes content in a block within a range.
 68+ *
 69+ * @method
 70+ * @param range {es.Range} Range of content to remove
 71+ */
 72+es.ListBlock.prototype.deleteContent = function( range ) {
 73+ range.normalize();
 74+
 75+ var locationStart = this.list.getLocationFromOffset( range.start ),
 76+ locationEnd = this.list.getLocationFromOffset( range.end );
 77+
 78+ if ( locationStart.item == locationEnd.item ) {
 79+
 80+ // delete content within one item
 81+
 82+ locationStart.item.content.remove(
 83+ new es.Range( locationStart.offset, locationStart.offset + range.getLength() )
 84+ );
 85+
 86+ } else {
 87+
 88+ // delete content across multiple items
 89+
 90+ // delete selected content in first selected item
 91+ locationStart.item.content.remove(
 92+ new es.Range(
 93+ locationStart.offset,
 94+ locationStart.item.content.getLength()
 95+ )
 96+ );
 97+
 98+ // grab not selected content from last selected item..
 99+ var toAppend = locationEnd.item.content.getContent(
 100+ new es.Range(
 101+ locationEnd.offset,
 102+ locationEnd.item.content.getLength()
 103+ )
 104+ );
 105+
 106+ // ..and insert it at the end of first selected item
 107+ locationStart.item.content.insert( locationStart.offset, toAppend.data );
 108+
 109+ var toDelete = [],
 110+ toAdopt = [],
 111+ newParents = [],
 112+ toDeleteCollecting = false,
 113+ toAdoptCollecting = false,
 114+ itemLevel,
 115+ toAdoptLevel,
 116+ newParent,
 117+ startItemLevel = locationStart.item.getLevel();
 118+
 119+ this.traverseItems( function( item, index ) {
 120+ itemLevel = item.getLevel();
 121+
 122+ if ( toDeleteCollecting === false && toDelete.length === 0 ) {
 123+ // collect items from before the selection as possible parents for items that may need adoption
 124+ // only one item per level
 125+ newParents[ itemLevel ] = {
 126+ item: item,
 127+ child: item.lists[0] ? item.lists[0].first() : null
 128+ };
 129+ newParents = newParents.slice( 0, itemLevel + 1 );
 130+ }
 131+
 132+ if ( toDeleteCollecting ) {
 133+ // keep collecting items for deletion
 134+ toDelete.push( item );
 135+ }
 136+
 137+ if ( item == locationStart.item ) {
 138+ // start collecting items for deletion
 139+ toDeleteCollecting = true;
 140+ }
 141+
 142+ if ( item == locationEnd.item ) {
 143+ // stop collecting items for deletion
 144+ // start collecting items for adoption
 145+ toDeleteCollecting = false;
 146+ toAdoptCollecting = true;
 147+ return true;
 148+ }
 149+
 150+ if ( toAdoptCollecting ) {
 151+ // collect items for adoption
 152+ // only those which parents will be removed
 153+ if( toDelete.indexOf ( item.list.item ) !== -1 ) {
 154+ toAdopt.push( item );
 155+ }
 156+ }
 157+
 158+ } );
 159+
 160+ for ( var i = 0; i < toAdopt.length; i++ ) {
 161+ toAdoptLevel = toAdopt[i].getLevel();
 162+
 163+ // determine new parent for item to adopt
 164+ if ( newParents[ toAdoptLevel - 1 ] ) {
 165+ newParent = newParents[ toAdoptLevel - 1 ];
 166+ } else {
 167+ newParent = newParents[ newParents.length - 1 ]
 168+ }
 169+
 170+ if( newParent.child && toAdoptLevel > startItemLevel ) {
 171+ newParent.item.lists[0].insertBefore( toAdopt[i], newParent.child );
 172+ } else {
 173+ if ( newParent.item.lists[0] ) {
 174+ newParent.item.lists[0].append( toAdopt[i] );
 175+ } else {
 176+ newParent.item.append(
 177+ new es.ListBlockList(
 178+ toAdopt[i].list.style,
 179+ [ toAdopt[i] ]
 180+ )
 181+ );
 182+ }
 183+ }
 184+ }
 185+
 186+ for ( var i = 0; i < toDelete.length; i++ ) {
 187+ toDelete[i].list.remove( toDelete[i] );
 188+ }
 189+ }
 190+};
 191+
 192+/**
 193+ * Applies an annotation to a given range.
 194+ *
 195+ * If a range arguments are not provided, all content will be annotated.
 196+ *
 197+ * @method
 198+ * @param method {String} Way to apply annotation ("toggle", "add" or "remove")
 199+ * @param annotation {Object} Annotation to apply
 200+ * @param range {es.Range} Range of content to annotate
 201+ */
 202+es.ListBlock.prototype.annotateContent = function( method, annotation, range ) {
 203+ range.normalize();
 204+ var locationStart = this.list.getLocationFromOffset( range.start ),
 205+ locationEnd = this.list.getLocationFromOffset( range.end );
 206+
 207+ if ( locationStart.item == locationEnd.item ) {
 208+ // annotating content within one item
 209+ locationStart.item.content.annotate(
 210+ method,
 211+ annotation,
 212+ new es.Range( locationStart.offset, locationStart.offset + range.end - range.start )
 213+ );
 214+ } else {
 215+ // annotating content across multiple items
 216+
 217+ // collect all items to be later annotate - you can not modify during traversing
 218+ var itemsToAnnotate;
 219+ this.traverseItems( function( item, index ) {
 220+ if ( item == locationEnd.item ) {
 221+ return false;
 222+ }
 223+ if ( $.isArray( itemsToAnnotate ) ) {
 224+ itemsToAnnotate.push( item );
 225+ }
 226+ if ( item == locationStart.item ) {
 227+ itemsToAnnotate = [];
 228+ }
 229+ } );
 230+
 231+ // annotate content in the first item - from offset to end
 232+ locationStart.item.content.annotate(
 233+ method,
 234+ annotation,
 235+ new es.Range( locationStart.offset, locationStart.item.content.getLength() )
 236+ );
 237+
 238+ // annotate content in the last item - from beginning to offset
 239+ locationEnd.item.content.annotate(
 240+ method,
 241+ annotation,
 242+ new es.Range( 0, locationEnd.offset )
 243+ );
 244+
 245+ // annotate content in all items in between first and last items
 246+ for ( var i = 0; i < itemsToAnnotate.length; i++ ) {
 247+ itemsToAnnotate[i].content.annotate(
 248+ method,
 249+ annotation,
 250+ new es.Range( 0, itemsToAnnotate[i].content.getLength() )
 251+ );
 252+ }
 253+ }
 254+};
 255+
 256+/**
 257+ * Gets content within a range.
 258+ *
 259+ * @method
 260+ * @param range {es.Range} Range of content to get
 261+ * @returns {es.Content} Content within range
 262+ */
 263+es.ListBlock.prototype.getContent = function( range ) {
 264+ // TODO: Implement me!
 265+ return new es.Content();
 266+};
 267+
 268+/**
 269+ * Gets content as plain text within a range.
 270+ *
 271+ * @method
 272+ * @param range {Range} Range of text to get
 273+ * @param render {Boolean} If annotations should have any influence on output
 274+ * @returns {String} Text within range
 275+ */
 276+es.ListBlock.prototype.getText = function( range ) {
 277+ // TODO: Implement me!
 278+ return '';
 279+};
 280+
 281+/**
 282+ * Renders content into a container.
 283+ *
 284+ * @method
 285+ * @param offset {Integer} Offset to render from, if possible
 286+ */
 287+es.ListBlock.prototype.renderContent = function( offset ) {
 288+ this.list.renderContent( offset );
 289+};
 290+
 291+/**
 292+ * Gets the offset of a position.
 293+ *
 294+ * @method
 295+ * @param position {es.Position} Position to translate
 296+ * @returns {Integer} Offset nearest to position
 297+ */
 298+es.ListBlock.prototype.getOffset = function( position ) {
 299+ if ( position.top < 0 ) {
 300+ return 0;
 301+ } else if ( position.top >= this.$.height() ) {
 302+ return this.getLength();
 303+ }
 304+ var blockOffset = this.$.offset();
 305+ position.top += blockOffset.top;
 306+ position.left += blockOffset.left;
 307+ return this.list.getOffsetFromPosition( position );
 308+};
 309+
 310+/**
 311+ * Gets the position of an offset.
 312+ *
 313+ * @method
 314+ * @param offset {Integer} Offset to translate
 315+ * @returns {es.Position} Position of offset
 316+ */
 317+es.ListBlock.prototype.getPosition = function( offset ) {
 318+ var location = this.list.getLocationFromOffset( offset )
 319+ position = location.item.flow.getPosition( location.offset ),
 320+ blockOffset = this.$.offset(),
 321+ lineOffset = location.item.$content.offset();
 322+
 323+ position.top += lineOffset.top - blockOffset.top;
 324+ position.left += lineOffset.left - blockOffset.left;
 325+ position.bottom += lineOffset.top - blockOffset.top;
 326+
 327+ this.traverseItems( function( item ) {
 328+ if ( item === location.item ) {
 329+ return false;
 330+ }
 331+ position.line += item.flow.lines.length;
 332+ } );
 333+ return position;
 334+};
 335+
 336+/**
 337+ * Gets the start and end points of the word closest a given offset.
 338+ *
 339+ * @method
 340+ * @param offset {Integer} Offset to find word nearest to
 341+ * @returns {Object} Range object of boundaries
 342+ */
 343+es.ListBlock.prototype.getWordBoundaries = function( offset ) {
 344+ var location = this.list.getLocationFromOffset( offset );
 345+ var boundaries = location.item.flow.content.getWordBoundaries( location.offset );
 346+ boundaries.start += offset - location.offset;
 347+ boundaries.end += offset - location.offset;
 348+ return boundaries;
 349+};
 350+
 351+/**
 352+ * Gets the start and end points of the section closest a given offset.
 353+ *
 354+ * @method
 355+ * @param offset {Integer} Offset to find section nearest to
 356+ * @returns {Object} Range object of boundaries
 357+ */
 358+es.ListBlock.prototype.getSectionBoundaries = function( offset ) {
 359+ var location = this.list.getLocationFromOffset( offset ),
 360+ start = offset - location.offset;
 361+ return new es.Range( start, start + location.item.content.getLength() );
 362+};
 363+
 364+es.ListBlock.prototype.getLineBoundaries = function( offset ) {
 365+ var location = this.list.getLocationFromOffset( offset ),
 366+ line;
 367+
 368+ for ( var i = 0; i < location.item.flow.lines.length; i++ ) {
 369+ line = location.item.flow.lines[i];
 370+ if ( location.offset >= line.range.start && location.offset < line.range.end ) {
 371+ break;
 372+ }
 373+ }
 374+
 375+ return new es.Range(
 376+ ( offset - location.offset ) + line.range.start,
 377+ ( offset - location.offset ) + ( line.range.end < location.item.content.getLength() ? line.range.end - 1 : line.range.end )
 378+ );
 379+};
 380+
 381+/**
 382+ * Iteratively execute a callback on each item in the list.
 383+ *
 384+ * Traversal is performed in a depth-first pattern, which is equivilant to a vertical scan of list
 385+ * items. To stop traversal, return false within the callback function.
 386+ *
 387+ * @method
 388+ * @param callback {Function} Function to execute for each item, accepts an item and index argument
 389+ * @returns {Boolean} Whether all items were traversed, or traversal was cut short
 390+ */
 391+es.ListBlock.prototype.traverseItems = function( callback ) {
 392+ var stack = [{ 'list': this.list, 'index': 0 }],
 393+ list,
 394+ item,
 395+ pop,
 396+ parent,
 397+ index = 0;
 398+ while ( stack.length ) {
 399+ iteration = stack[stack.length - 1];
 400+ pop = true;
 401+ while ( iteration.index < iteration.list.items.length ) {
 402+ item = iteration.list.items[iteration.index++];
 403+ if ( callback( item, index++ ) === false ) {
 404+ return false;
 405+ }
 406+ if ( item.lists.length ) {
 407+ parent = stack.length;
 408+ for ( var i = 0; i < item.lists.length; i++ ) {
 409+ stack.push( { 'list': item.lists[i], 'index': 0, 'parent': parent } );
 410+ }
 411+ pop = false;
 412+ break;
 413+ }
 414+ }
 415+ if ( pop ) {
 416+ if ( iteration.parent ) {
 417+ stack = stack.slice( 0, iteration.parent );
 418+ } else {
 419+ stack.pop();
 420+ }
 421+ }
 422+ }
 423+ return true;
 424+};
 425+
 426+/* Registration */
 427+
 428+/**
 429+ * Extend es.Block to support list block creation with es.Block.newFromWikiDom
 430+ */
 431+es.Block.blockConstructors.list = es.ListBlock.newFromWikiDomListBlock;
 432+
 433+/* Inheritance */
 434+
 435+es.extend( es.ListBlock, es.Block );
Property changes on: trunk/parsers/wikidom/lib/es/oldListBlock/es.ListBlock.js
___________________________________________________________________
Added: svn:eol-style
1436 + native
Index: trunk/parsers/wikidom/lib/es/es.ListBlockItem.js
@@ -1,190 +1,19 @@
2 -/**
3 - * List item.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {es.EventEmitter}
8 - * @extends {es.Container}
9 - * @param content {es.Content} Item content
10 - * @param lists {Array} List of item sub-lists
11 - * @property content {es.Content} Item content
12 - * @property lists {Array} List of item sub-lists
13 - * @property $line {jQuery} Line element
14 - * @property $content {jQuery} Content element
15 - * @property flow {es.Flow} Text flow object for content
16 - */
17 -es.ListBlockItem = function( content, lists ) {
 2+es.ListBlockItem = function( content, style, level ) {
183 es.EventEmitter.call( this );
19 - es.Container.call( this, 'item', 'lists', lists, 'li' );
20 -
 4+
215 this.content = content || new es.Content();
22 - this.$content = $( '<div class="editSurface-list-content"></div>' );
23 - this.$.prepend( this.$content );
24 - this.flow = new es.Flow( this.$content, this.content );
25 -
26 - /*
27 - this.content = content || new es.Content();
28 - this.$line = $( '<div class="editSurface-list-line"></div>' );
29 - this.$content = $( '<div class="editSurface-list-content"></div>' );
30 - this.$.prepend( this.$line.append( this.$content ) );
31 - this.flow = new es.Flow( this.$content, this.content );
32 - */
33 -
 6+ this.$ = $( '<div class="editSurface-list-content"></div>' )
 7+ .addClass( 'editSurface-item-level' + level );
 8+ this.flow = new es.Flow( this.$, this.content );
 9+
3410 var listBlockItem = this;
3511 this.flow.on( 'render', function() {
3612 listBlockItem.emit( 'update' );
3713 } );
3814 }
3915
40 -/* Static Methods */
41 -
42 -/**
43 - * Creates an EditSurface list item object from a WikiDom list item object.
44 - *
45 - * @static
46 - * @method
47 - * @param wikidomListItem {Object} WikiDom list item
48 - * @returns {es.ListBlockItem} EditSurface list block item
49 - */
50 -es.ListBlockItem.newFromWikiDomListItem = function( wikidomListItem ) {
51 - // Convert items to es.ListBlockItem objects
52 - var lists = [];
53 - if ( wikidomListItem.lists ) {
54 - for ( var i = 0; i < wikidomListItem.lists.length; i++ ) {
55 - lists.push( es.ListBlockList.newFromWikiDomList( wikidomListItem.lists[i] ) );
56 - }
57 - }
58 - return new es.ListBlockItem( es.Content.newFromWikiDomLine( wikidomListItem.line ), lists );
59 -};
60 -
61 -/* Methods */
62 -
63 -/**
64 - * Gets the index of the item within it's list.
65 - *
66 - * TODO: Move to es.Container
67 - *
68 - * @method
69 - * @returns {Integer} Index of item
70 - */
71 -es.ListBlockItem.prototype.getIndex = function() {
72 - return this.list._list.indexOf( this );
73 -};
74 -
75 -/**
76 - * Gets the length of content in both the line and sub-lists.
77 - *
78 - * @method
79 - * @returns {Integer} Length of content
80 - */
81 -es.ListBlockItem.prototype.getLength = function() {
82 - var length = this.content.getLength() + 1;
83 - for ( var i = 0; i < this.lists.length; i++ ) {
84 - length += this.lists[i].getLength();
85 - }
86 - return length;
87 -};
88 -
89 -/**
90 - * Gets a location from an offset.
91 - *
92 - * @method
93 - * @param offset {Integer} Offset to get location for
94 - * @returns {Object} Location object with item and offset properties, where offset is local
95 - * to item.
96 - */
97 -es.ListBlockItem.prototype.getLocationFromOffset = function( offset ) {
98 - var contentLength = this.content.getLength() + 1;
99 - if ( offset < contentLength ) {
100 - return {
101 - 'item': this,
102 - 'offset': offset
103 - };
104 - }
105 - offset -= contentLength;
106 - var listOffset = 0,
107 - listLength;
108 - for ( var i = 0; i < this.lists.length; i++ ) {
109 - listLength = this.lists[i].getLength();
110 - if ( offset >= listOffset && offset < listOffset + listLength ) {
111 - return this.lists[i].getLocationFromOffset( offset - listOffset );
112 - }
113 - listOffset += listLength;
114 - }
115 -};
116 -
117 -/**
118 - * Gets an offset within the item from a position.
119 - *
120 - * @method
121 - * @param position {es.Position} Position to translate
122 - * @returns {Integer} Offset nearest position
123 - * @returns {Null} If offset could not be found
124 - */
125 -es.ListBlockItem.prototype.getOffsetFromPosition = function( position ) {
126 - var itemOffset = this.$.offset(),
127 - itemHeight = this.$.height(),
128 - offset = null,
129 - globalOffset = null;
130 -
131 - if ( position.top >= itemOffset.top && position.top < itemOffset.top + itemHeight ) {
132 - if ( position.top < itemOffset.top + this.$content.height() ) {
133 - position.top -= itemOffset.top;
134 - position.left -= itemOffset.left;
135 - return globalOffset + this.flow.getOffset( position );
136 - }
137 -
138 - for ( var i = 0; i < this.lists.length; i++ ) {
139 - offset = this.lists[i].getOffsetFromPosition( position );
140 - if ( offset != null ) {
141 - return globalOffset + offset + this.content.getLength() + 1;
142 - } else {
143 - globalOffset += this.lists[i].getLength();
144 - }
145 - }
146 - }
147 - return null;
148 -};
149 -
150 -/**
151 - * Gets a depth level for the item in list.
152 - * Example:
153 - *
154 - * # level 0
155 - * ## level 1
156 - * # level 0
157 - * ### level 2
158 - *
159 - * @method
160 - * @returns {Integer} Depth level
161 - */
162 -es.ListBlockItem.prototype.getLevel = function( position ) {
163 - var start = this.list.item,
164 - level = 0;
165 -
166 - while ( start ) {
167 - start = start.list.item;
168 - level++;
169 - }
170 -
171 - return level;
172 -};
173 -
174 -/**
175 - * Renders content and sub-lists.
176 - *
177 - * @method
178 - * @param offset {Integer} Offset to render from if possible
179 - */
18016 es.ListBlockItem.prototype.renderContent = function( offset ) {
181 - // TODO: Abstract offset and use it when rendering
18217 this.flow.render();
183 - for ( var i = 0; i < this.lists.length; i++ ) {
184 - this.lists[i].renderContent();
185 - }
18618 };
18719
188 -/* Inheritance */
189 -
190 -es.extend( es.ListBlockItem, es.EventEmitter );
191 -es.extend( es.ListBlockItem, es.Container );
 20+es.extend( es.ListBlockItem, es.EventEmitter );
\ No newline at end of file
Index: trunk/parsers/wikidom/lib/es/es.ListBlockList.js
@@ -1,115 +1,38 @@
2 -/**
3 - * Number or bullet list.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {es.EventEmitter}
8 - * @extends {es.Container}
9 - * @param style {String} Style of list, either "number" or "bullet"
10 - * @param items {Array} List of es.ListBlockItem objects to initialize list with
11 - * @property style {String} Style of list
12 - * @property items {Array} List of es.ListBlockItem objects
13 - */
14 -es.ListBlockList = function( style, items ) {
 2+es.ListBlockList = function( items ) {
153 es.EventEmitter.call( this );
16 - es.Container.call( this, 'list', 'items', items, 'ul' );
17 - this.style = style || 'bullet';
18 - if ( this.style == 'number' ) {
19 - this.$.css('list-style', 'decimal');
20 - }
 4+ es.Container.call( this, 'list', 'items', items );
215 };
226
23 -/* Static Methods */
24 -
25 -/**
26 - * Creates an EditSurface list object from a WikiDom list object.
27 - *
28 - * @static
29 - * @method
30 - * @param wikidomListItem {Object} WikiDom list item
31 - * @returns {es.ListBlockItem} EditSurface list block item
32 - */
337 es.ListBlockList.newFromWikiDomList = function( wikidomList ) {
348 var items = [];
35 - for ( var i = 0; i < wikidomList.items.length; i++ ) {
36 - items.push( es.ListBlockItem.newFromWikiDomListItem( wikidomList.items[i] ) );
37 - }
38 - return new es.ListBlockList( wikidomList.style, items );
 9+ es.ListBlockList.flattenList( wikidomList, items, 0 );
 10+ return new es.ListBlockList( items );
3911 };
4012
41 -/* Methods */
42 -
43 -/**
44 - * Gets the length of content in both the line and sub-lists.
45 - *
46 - * @method
47 - * @returns {Integer} Length of content
48 - */
49 -es.ListBlockList.prototype.getLength = function() {
50 - var length = 0;
 13+es.ListBlockList.prototype.renderContent = function( offset ) {
5114 for ( var i = 0; i < this.items.length; i++ ) {
52 - length += this.items[i].getLength();
 15+ this.items[i].renderContent();
5316 }
54 - return length;
5517 };
5618
57 -/**
58 - * Gets a location from an offset.
59 - *
60 - * @method
61 - * @param offset {Integer} Offset to get location for
62 - * @returns {Object} Location object with item and offset properties, where offset is local
63 - * to item.
64 - */
65 -es.ListBlockList.prototype.getLocationFromOffset = function( offset ) {
66 - var itemOffset = 0,
67 - itemLength;
68 - for ( var i = 0; i < this.items.length; i++ ) {
69 - itemLength = this.items[i].getLength();
70 - if ( offset >= itemOffset && offset < itemOffset + itemLength ) {
71 - return this.items[i].getLocationFromOffset( offset - itemOffset );
 19+es.ListBlockList.flattenList = function( wikidomList, items, level ) {
 20+ for ( var i = 0; i < wikidomList.items.length; i++ ) {
 21+ items.push(
 22+ new es.ListBlockItem(
 23+ es.Content.newFromWikiDomLine( wikidomList.items[i].line ),
 24+ wikidomList.style,
 25+ level
 26+ )
 27+ );
 28+ if ( wikidomList.items[i].lists ) {
 29+ level++;
 30+ for ( var j = 0; j < wikidomList.items[i].lists.length; j++ ) {
 31+ es.ListBlockList.flattenList( wikidomList.items[i].lists[j], items, level );
 32+ }
 33+ level--;
7234 }
73 - itemOffset += itemLength;
7435 }
7536 };
7637
77 -/**
78 - * Gets an offset within the list from a position.
79 - *
80 - * @method
81 - * @param position {es.Position} Position to translate
82 - * @returns {Integer} Offset nearest position
83 - * @returns {Null} If offset could not be found
84 - */
85 -es.ListBlockList.prototype.getOffsetFromPosition = function( position ) {
86 - var itemOffset = null,
87 - globalOffset = null;
88 -
89 - for ( var i = 0; i < this.items.length; i++ ) {
90 - itemOffset = this.items[i].getOffsetFromPosition( position );
91 -
92 - if ( itemOffset !== null ) {
93 - return globalOffset + itemOffset;
94 - } else {
95 - globalOffset += this.items[i].getLength();
96 - }
97 - }
98 -};
99 -
100 -/**
101 - * Renders items.
102 - *
103 - * @method
104 - * @param offset {Integer} Offset to render from if possible
105 - */
106 -es.ListBlockList.prototype.renderContent = function( offset ) {
107 - // TODO: Abstract offset and use it when rendering
108 - for ( var i = 0; i < this.items.length; i++ ) {
109 - this.items[i].renderContent();
110 - }
111 -};
112 -
113 -/* Inheritance */
114 -
11538 es.extend( es.ListBlockList, es.EventEmitter );
11639 es.extend( es.ListBlockList, es.Container );
Index: trunk/parsers/wikidom/lib/es/es.ListBlock.js
@@ -1,35 +1,18 @@
2 -/**
3 - * Creates a list block.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {es.Block}
8 - * @param list {es.ListBlockList} Root list to initialize with
9 - * @property list {es.ListBlockList}
10 - * @property $ {jQuery}
11 - */
122 es.ListBlock = function( list ) {
133 es.Block.call( this );
 4+
145 this.list = list || new es.ListBlockList();
 6+
157 this.$ = this.list.$
168 .addClass( 'editSurface-block' )
179 .data( 'block', this );
18 - var listBlock = this;
 10+
 11+ var listBlock = this;
1912 this.list.on( 'update', function() {
2013 listBlock.emit( 'update' );
2114 } );
2215 };
2316
24 -/* Static Methods */
25 -
26 -/**
27 - * Creates a new list block object from WikiDom data.
28 - *
29 - * @static
30 - * @method
31 - * @param wikidomParagraphBlock {Object} WikiDom data to convert from
32 - * @returns {es.ListBlock} EditSurface list block
33 - */
3417 es.ListBlock.newFromWikiDomListBlock = function( wikidomListBlock ) {
3518 if ( wikidomListBlock.type !== 'list' ) {
3619 throw 'Invalid block type error. List block expected to be of type "list".';
@@ -37,398 +20,9 @@
3821 return new es.ListBlock( es.ListBlockList.newFromWikiDomList( wikidomListBlock ) );
3922 };
4023
41 -/* Methods */
42 -
43 -/**
44 - * Gets the length of all block content.
45 - *
46 - * @method
47 - * @returns {Integer} Length of content
48 - */
49 -es.ListBlock.prototype.getLength = function() {
50 - // Compensate for n+1 virtual position on the last item's content
51 - return this.list.getLength() - 1;
52 -};
53 -
54 -/**
55 - * Inserts content into a block at an offset.
56 - *
57 - * @method
58 - * @param offset {Integer} Position to insert content at
59 - * @param content {Object} Content to insert
60 - */
61 -es.ListBlock.prototype.insertContent = function( offset, content ) {
62 - var location = this.list.getLocationFromOffset( offset );
63 - location.item.flow.content.insert( location.offset, content );
64 -};
65 -
66 -/**
67 - * Deletes content in a block within a range.
68 - *
69 - * @method
70 - * @param range {es.Range} Range of content to remove
71 - */
72 -es.ListBlock.prototype.deleteContent = function( range ) {
73 - range.normalize();
74 -
75 - var locationStart = this.list.getLocationFromOffset( range.start ),
76 - locationEnd = this.list.getLocationFromOffset( range.end );
77 -
78 - if ( locationStart.item == locationEnd.item ) {
79 -
80 - // delete content within one item
81 -
82 - locationStart.item.content.remove(
83 - new es.Range( locationStart.offset, locationStart.offset + range.getLength() )
84 - );
85 -
86 - } else {
87 -
88 - // delete content across multiple items
89 -
90 - // delete selected content in first selected item
91 - locationStart.item.content.remove(
92 - new es.Range(
93 - locationStart.offset,
94 - locationStart.item.content.getLength()
95 - )
96 - );
97 -
98 - // grab not selected content from last selected item..
99 - var toAppend = locationEnd.item.content.getContent(
100 - new es.Range(
101 - locationEnd.offset,
102 - locationEnd.item.content.getLength()
103 - )
104 - );
105 -
106 - // ..and insert it at the end of first selected item
107 - locationStart.item.content.insert( locationStart.offset, toAppend.data );
108 -
109 - var toDelete = [],
110 - toAdopt = [],
111 - newParents = [],
112 - toDeleteCollecting = false,
113 - toAdoptCollecting = false,
114 - itemLevel,
115 - toAdoptLevel,
116 - newParent,
117 - startItemLevel = locationStart.item.getLevel();
118 -
119 - this.traverseItems( function( item, index ) {
120 - itemLevel = item.getLevel();
121 -
122 - if ( toDeleteCollecting === false && toDelete.length === 0 ) {
123 - // collect items from before the selection as possible parents for items that may need adoption
124 - // only one item per level
125 - newParents[ itemLevel ] = {
126 - item: item,
127 - child: item.lists[0] ? item.lists[0].first() : null
128 - };
129 - newParents = newParents.slice( 0, itemLevel + 1 );
130 - }
131 -
132 - if ( toDeleteCollecting ) {
133 - // keep collecting items for deletion
134 - toDelete.push( item );
135 - }
136 -
137 - if ( item == locationStart.item ) {
138 - // start collecting items for deletion
139 - toDeleteCollecting = true;
140 - }
141 -
142 - if ( item == locationEnd.item ) {
143 - // stop collecting items for deletion
144 - // start collecting items for adoption
145 - toDeleteCollecting = false;
146 - toAdoptCollecting = true;
147 - return true;
148 - }
149 -
150 - if ( toAdoptCollecting ) {
151 - // collect items for adoption
152 - // only those which parents will be removed
153 - if( toDelete.indexOf ( item.list.item ) !== -1 ) {
154 - toAdopt.push( item );
155 - }
156 - }
157 -
158 - } );
159 -
160 - for ( var i = 0; i < toAdopt.length; i++ ) {
161 - toAdoptLevel = toAdopt[i].getLevel();
162 -
163 - // determine new parent for item to adopt
164 - if ( newParents[ toAdoptLevel - 1 ] ) {
165 - newParent = newParents[ toAdoptLevel - 1 ];
166 - } else {
167 - newParent = newParents[ newParents.length - 1 ]
168 - }
169 -
170 - if( newParent.child && toAdoptLevel > startItemLevel ) {
171 - newParent.item.lists[0].insertBefore( toAdopt[i], newParent.child );
172 - } else {
173 - if ( newParent.item.lists[0] ) {
174 - newParent.item.lists[0].append( toAdopt[i] );
175 - } else {
176 - newParent.item.append(
177 - new es.ListBlockList(
178 - toAdopt[i].list.style,
179 - [ toAdopt[i] ]
180 - )
181 - );
182 - }
183 - }
184 - }
185 -
186 - for ( var i = 0; i < toDelete.length; i++ ) {
187 - toDelete[i].list.remove( toDelete[i] );
188 - }
189 - }
190 -};
191 -
192 -/**
193 - * Applies an annotation to a given range.
194 - *
195 - * If a range arguments are not provided, all content will be annotated.
196 - *
197 - * @method
198 - * @param method {String} Way to apply annotation ("toggle", "add" or "remove")
199 - * @param annotation {Object} Annotation to apply
200 - * @param range {es.Range} Range of content to annotate
201 - */
202 -es.ListBlock.prototype.annotateContent = function( method, annotation, range ) {
203 - range.normalize();
204 - var locationStart = this.list.getLocationFromOffset( range.start ),
205 - locationEnd = this.list.getLocationFromOffset( range.end );
206 -
207 - if ( locationStart.item == locationEnd.item ) {
208 - // annotating content within one item
209 - locationStart.item.content.annotate(
210 - method,
211 - annotation,
212 - new es.Range( locationStart.offset, locationStart.offset + range.end - range.start )
213 - );
214 - } else {
215 - // annotating content across multiple items
216 -
217 - // collect all items to be later annotate - you can not modify during traversing
218 - var itemsToAnnotate;
219 - this.traverseItems( function( item, index ) {
220 - if ( item == locationEnd.item ) {
221 - return false;
222 - }
223 - if ( $.isArray( itemsToAnnotate ) ) {
224 - itemsToAnnotate.push( item );
225 - }
226 - if ( item == locationStart.item ) {
227 - itemsToAnnotate = [];
228 - }
229 - } );
230 -
231 - // annotate content in the first item - from offset to end
232 - locationStart.item.content.annotate(
233 - method,
234 - annotation,
235 - new es.Range( locationStart.offset, locationStart.item.content.getLength() )
236 - );
237 -
238 - // annotate content in the last item - from beginning to offset
239 - locationEnd.item.content.annotate(
240 - method,
241 - annotation,
242 - new es.Range( 0, locationEnd.offset )
243 - );
244 -
245 - // annotate content in all items in between first and last items
246 - for ( var i = 0; i < itemsToAnnotate.length; i++ ) {
247 - itemsToAnnotate[i].content.annotate(
248 - method,
249 - annotation,
250 - new es.Range( 0, itemsToAnnotate[i].content.getLength() )
251 - );
252 - }
253 - }
254 -};
255 -
256 -/**
257 - * Gets content within a range.
258 - *
259 - * @method
260 - * @param range {es.Range} Range of content to get
261 - * @returns {es.Content} Content within range
262 - */
263 -es.ListBlock.prototype.getContent = function( range ) {
264 - // TODO: Implement me!
265 - return new es.Content();
266 -};
267 -
268 -/**
269 - * Gets content as plain text within a range.
270 - *
271 - * @method
272 - * @param range {Range} Range of text to get
273 - * @param render {Boolean} If annotations should have any influence on output
274 - * @returns {String} Text within range
275 - */
276 -es.ListBlock.prototype.getText = function( range ) {
277 - // TODO: Implement me!
278 - return '';
279 -};
280 -
281 -/**
282 - * Renders content into a container.
283 - *
284 - * @method
285 - * @param offset {Integer} Offset to render from, if possible
286 - */
28724 es.ListBlock.prototype.renderContent = function( offset ) {
28825 this.list.renderContent( offset );
28926 };
29027
291 -/**
292 - * Gets the offset of a position.
293 - *
294 - * @method
295 - * @param position {es.Position} Position to translate
296 - * @returns {Integer} Offset nearest to position
297 - */
298 -es.ListBlock.prototype.getOffset = function( position ) {
299 - if ( position.top < 0 ) {
300 - return 0;
301 - } else if ( position.top >= this.$.height() ) {
302 - return this.getLength();
303 - }
304 - var blockOffset = this.$.offset();
305 - position.top += blockOffset.top;
306 - position.left += blockOffset.left;
307 - return this.list.getOffsetFromPosition( position );
308 -};
309 -
310 -/**
311 - * Gets the position of an offset.
312 - *
313 - * @method
314 - * @param offset {Integer} Offset to translate
315 - * @returns {es.Position} Position of offset
316 - */
317 -es.ListBlock.prototype.getPosition = function( offset ) {
318 - var location = this.list.getLocationFromOffset( offset )
319 - position = location.item.flow.getPosition( location.offset ),
320 - blockOffset = this.$.offset(),
321 - lineOffset = location.item.$content.offset();
322 -
323 - position.top += lineOffset.top - blockOffset.top;
324 - position.left += lineOffset.left - blockOffset.left;
325 - position.bottom += lineOffset.top - blockOffset.top;
326 -
327 - this.traverseItems( function( item ) {
328 - if ( item === location.item ) {
329 - return false;
330 - }
331 - position.line += item.flow.lines.length;
332 - } );
333 - return position;
334 -};
335 -
336 -/**
337 - * Gets the start and end points of the word closest a given offset.
338 - *
339 - * @method
340 - * @param offset {Integer} Offset to find word nearest to
341 - * @returns {Object} Range object of boundaries
342 - */
343 -es.ListBlock.prototype.getWordBoundaries = function( offset ) {
344 - var location = this.list.getLocationFromOffset( offset );
345 - var boundaries = location.item.flow.content.getWordBoundaries( location.offset );
346 - boundaries.start += offset - location.offset;
347 - boundaries.end += offset - location.offset;
348 - return boundaries;
349 -};
350 -
351 -/**
352 - * Gets the start and end points of the section closest a given offset.
353 - *
354 - * @method
355 - * @param offset {Integer} Offset to find section nearest to
356 - * @returns {Object} Range object of boundaries
357 - */
358 -es.ListBlock.prototype.getSectionBoundaries = function( offset ) {
359 - var location = this.list.getLocationFromOffset( offset ),
360 - start = offset - location.offset;
361 - return new es.Range( start, start + location.item.content.getLength() );
362 -};
363 -
364 -es.ListBlock.prototype.getLineBoundaries = function( offset ) {
365 - var location = this.list.getLocationFromOffset( offset ),
366 - line;
367 -
368 - for ( var i = 0; i < location.item.flow.lines.length; i++ ) {
369 - line = location.item.flow.lines[i];
370 - if ( location.offset >= line.range.start && location.offset < line.range.end ) {
371 - break;
372 - }
373 - }
374 -
375 - return new es.Range(
376 - ( offset - location.offset ) + line.range.start,
377 - ( offset - location.offset ) + ( line.range.end < location.item.content.getLength() ? line.range.end - 1 : line.range.end )
378 - );
379 -};
380 -
381 -/**
382 - * Iteratively execute a callback on each item in the list.
383 - *
384 - * Traversal is performed in a depth-first pattern, which is equivilant to a vertical scan of list
385 - * items. To stop traversal, return false within the callback function.
386 - *
387 - * @method
388 - * @param callback {Function} Function to execute for each item, accepts an item and index argument
389 - * @returns {Boolean} Whether all items were traversed, or traversal was cut short
390 - */
391 -es.ListBlock.prototype.traverseItems = function( callback ) {
392 - var stack = [{ 'list': this.list, 'index': 0 }],
393 - list,
394 - item,
395 - pop,
396 - parent,
397 - index = 0;
398 - while ( stack.length ) {
399 - iteration = stack[stack.length - 1];
400 - pop = true;
401 - while ( iteration.index < iteration.list.items.length ) {
402 - item = iteration.list.items[iteration.index++];
403 - if ( callback( item, index++ ) === false ) {
404 - return false;
405 - }
406 - if ( item.lists.length ) {
407 - parent = stack.length;
408 - for ( var i = 0; i < item.lists.length; i++ ) {
409 - stack.push( { 'list': item.lists[i], 'index': 0, 'parent': parent } );
410 - }
411 - pop = false;
412 - break;
413 - }
414 - }
415 - if ( pop ) {
416 - if ( iteration.parent ) {
417 - stack = stack.slice( 0, iteration.parent );
418 - } else {
419 - stack.pop();
420 - }
421 - }
422 - }
423 - return true;
424 -};
425 -
426 -/* Registration */
427 -
428 -/**
429 - * Extend es.Block to support list block creation with es.Block.newFromWikiDom
430 - */
43128 es.Block.blockConstructors.list = es.ListBlock.newFromWikiDomListBlock;
432 -
433 -/* Inheritance */
434 -
43529 es.extend( es.ListBlock, es.Block );

Status & tagging log