r97977 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r97976‎ | r97977 | r97978 >
Date:00:14, 24 September 2011
Author:tparscal
Status:deferred
Tags:
Comment:
Added hype code, this is just temporary while we sort out how this will work, then we will merge it into synth
Modified paths:
  • /trunk/parsers/wikidom/lib/hype (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/es.DocumentModel.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/es.js (added) (history)
  • /trunk/parsers/wikidom/lib/hype/models (added) (history)

Diff [purge]

Index: trunk/parsers/wikidom/lib/hype/es.DocumentModel.js
@@ -0,0 +1,223 @@
 2+/**
 3+ * @class
 4+ * @constructor
 5+ */
 6+es.DocumentModel = function() {
 7+ this.data = [];
 8+};
 9+
 10+/*
 11+// High-level operations
 12+
 13+es.DocumentModel.prototype.prepareInsertContent = function( offset, content ) {
 14+ // retain ^ .. offset
 15+ // insert content
 16+ // retain offset .. $
 17+};
 18+es.DocumentModel.prototype.prepareRemoveContent = function( range ) {
 19+ // retain ^ .. range.start
 20+ // retain range.end .. $
 21+};
 22+es.DocumentModel.prototype.prepareAnnotateContent = function( range, annotations ) {
 23+ // retain ^ .. range.start
 24+ // start annotations
 25+ // retain range.start .. range.end
 26+ // end annotations
 27+ // retain range.end .. $
 28+};
 29+es.DocumentModel.prototype.prepareInsertBlock = function( offset, type, attributes, content ) {
 30+ // retain ^ .. offset
 31+ // insert elementStart (type, attributes)
 32+ // insert content
 33+ // insert elementEnd (type, attributes)
 34+ // retain offset .. $
 35+};
 36+es.DocumentModel.prototype.prepareRemoveBlock = function( offset ) {
 37+ // retain ^ .. offset
 38+ // retain findMatchingElementEnd( offset ) .. $
 39+};
 40+*/
 41+
 42+// Low-level operations
 43+
 44+es.DocumentModel.newFromPlainObject = function( obj ) {
 45+
 46+};
 47+
 48+es.DocumentModel.prototype.toPlainObject = function() {
 49+
 50+};
 51+
 52+es.DocumentModel.prototype.insertContent = function( offset, content ) {
 53+ this.data = this.data.slice( 0, offset ).concat( content ).concat( this.data.slice( offset ) );
 54+};
 55+
 56+es.DocumentModel.prototype.removeContent = function( range ) {
 57+ this.data.splice( range.start, range.end - range.start );
 58+};
 59+
 60+es.DocumentModel.prototype.annotateContent = function( range, annotations ) {
 61+ for ( var i = 0; i < annotations.length; i++ ) {
 62+ var annotation = annotations[i];
 63+ if ( annotation.action = 'add' ) {
 64+ // this.data[i][?]
 65+ } else if ( annotation.action = 'remove' ) {
 66+ // this.data[i][?]
 67+ }
 68+ }
 69+};
 70+
 71+es.DocumentModel.prototype.insertElement = function( offset, element ) {
 72+ this.data.splice( offset, 0, element );
 73+};
 74+
 75+es.DocumentModel.prototype.removeElement = function( offset ) {
 76+ this.data.splice( offset, 1 );
 77+};
 78+
 79+es.DocumentModel.prototype.annotateElement = function( offset, annotations ) {
 80+ for ( var i = 0; i < annotations.length; i++ ) {
 81+ var annotation = annotations[i];
 82+ if ( annotation.action = 'add' ) {
 83+ // this.data[i].annotations[?]
 84+ } else if ( annotation.action = 'remove' ) {
 85+ // this.data[i].annotations[?]
 86+ }
 87+ }
 88+};
 89+
 90+/*
 91+ * Example of content data
 92+ *
 93+ * Content data is an array made up of 3 kinds of values:
 94+ * String: Plain text character
 95+ * Array: Annotated character
 96+ * Object: Opening or closing structural element
 97+ */
 98+var data = [
 99+ // 0 - Beginning of paragraph
 100+ { 'type': 'paragraph' },
 101+ // 1 - Plain content
 102+ 'a',
 103+ // 2 - Annotated content
 104+ ['b', { 'type': 'bold' }],
 105+ // 3 - Annotated content
 106+ ['c', { 'type': 'italic' }],
 107+ // 4 - End of paragraph
 108+ { 'type': '/paragraph' }
 109+ // 5 - Beginning of table
 110+ { 'type': 'table' },
 111+ // 6 - Beginning of row
 112+ { 'type': 'row' },
 113+ // 7 - Beginning of cell
 114+ { 'type': 'cell' },
 115+ // 8 - Beginning of paragraph
 116+ { 'type': 'paragraph' },
 117+ // 9 - Plain content
 118+ 'a',
 119+ // 10 - End of paragraph
 120+ { 'type': '/paragraph' },
 121+ // 11 - Beginning of list
 122+ { 'type': 'list' },
 123+ // 12 - Beginning of bullet list item
 124+ { 'type': 'item', 'styles': ['bullet'] },
 125+ // 13 - Plain content
 126+ 'a',
 127+ // 14 - End of item
 128+ { 'type': '/item' },
 129+ // 15 - Beginning of nested bullet list item
 130+ { 'type': 'item', 'styles': ['bullet', 'bullet'] },
 131+ // 16 - Plain content
 132+ 'b',
 133+ // 17 - End of item
 134+ { 'type': '/item' },
 135+ // 18 - Beginning of numbered list item
 136+ { 'type': 'item', 'styles': ['number'] },
 137+ // 19 - Plain content
 138+ 'c',
 139+ // 20 - End of item
 140+ { 'type': '/item' },
 141+ // 21 - End of list
 142+ { 'type': '/list' },
 143+ // 22 - End of cell
 144+ { 'type': '/cell' }
 145+ // 23 - End of row
 146+ { 'type': '/row' }
 147+ // 24 - End of table
 148+ { 'type': '/table' }
 149+ // 25 - Beginning of paragraph
 150+ { 'type': 'paragraph' },
 151+ // 26 - Plain content
 152+ 'a'
 153+ // 27 - End of paragraph
 154+ { 'type': '/paragraph' },
 155+];
 156+
 157+/*
 158+ * Example of content tree
 159+ *
 160+ * Content trees are kept in sync with content data, providing a mapping between a structured user
 161+ * interface and a flat content model. They are made up of nodes which have some common properties:
 162+ * type: Symbolic name of a block or sub-block component
 163+ * length: Number of elements in content data between the element start and end
 164+ * items: Information about the content between the element start and end
 165+ */
 166+var tree = [
 167+ {
 168+ 'type': 'paragraph',
 169+ 'length': 5,
 170+ //'content': ['a', ['b', { 'type': 'bold' }], ['c', { 'type': 'italic' }]],
 171+ },
 172+ {
 173+ 'type': 'table',
 174+ 'length': 19,
 175+ 'items': [
 176+ {
 177+ 'type': 'row',
 178+ 'length': 17,
 179+ 'items': [
 180+ {
 181+ 'type': 'cell',
 182+ 'length': 15,
 183+ 'items': {
 184+ {
 185+ 'type': 'paragraph',
 186+ 'length': 3
 187+ //'content': ['a']
 188+ },
 189+ {
 190+ 'type': 'list',
 191+ 'length': 12,
 192+ 'items': [
 193+ {
 194+ 'type': 'item',
 195+ 'styles': ['bullet'],
 196+ 'length': 3,
 197+ //'content': ['a']
 198+ },
 199+ {
 200+ 'type': 'item',
 201+ 'styles': ['bullet', 'bullet'],
 202+ 'length': 3,
 203+ //'content': ['b']
 204+ },
 205+ {
 206+ 'type': 'item',
 207+ 'styles': ['number'],
 208+ 'length': 3,
 209+ //'content': ['c']
 210+ }
 211+ ]
 212+ }
 213+ }
 214+ }
 215+ ]
 216+ }
 217+ ]
 218+ },
 219+ {
 220+ 'type': 'paragraph',
 221+ 'length': 3,
 222+ //'content': ['a']
 223+ }
 224+];
Index: trunk/parsers/wikidom/lib/hype/es.js
@@ -0,0 +1,96 @@
 2+/**
 3+ * EditSurface namespace.
 4+ *
 5+ * All classes and functions will be attached to this object to keep the global namespace clean.
 6+ */
 7+var es = {};
 8+
 9+/* Functions */
 10+
 11+/**
 12+ * Extends a constructor with the prototype of another.
 13+ *
 14+ * When using this, it's required to include a call to the constructor of the parent class as the
 15+ * first code in the child class's constructor.
 16+ *
 17+ * @example
 18+ * // Define parent class
 19+ * function Foo() {
 20+ * // code here
 21+ * }
 22+ * // Define child class
 23+ * function Bar() {
 24+ * // Call parent constructor
 25+ * Foo.call( this );
 26+ * }
 27+ * // Extend prototype
 28+ * extend( Bar, Foo );
 29+ *
 30+ * @static
 31+ * @method
 32+ * @param dst {Function} Class to extend
 33+ * @param src {Function} Base class to use methods from
 34+ */
 35+es.extend = function( dst, src ) {
 36+ var base = new src();
 37+ for ( var method in base ) {
 38+ if ( typeof base[method] === 'function' && !( method in dst.prototype ) ) {
 39+ dst.prototype[method] = base[method];
 40+ }
 41+ }
 42+};
 43+
 44+/**
 45+ * Recursively compares string and number property between two objects.
 46+ *
 47+ * A false result may be caused by property inequality or by properties in one object missing from
 48+ * the other. An asymmetrical test may also be performed, which checks only that properties in the
 49+ * first object are present in the second object, but not the inverse.
 50+ *
 51+ * @static
 52+ * @method
 53+ * @param a {Object} First object to compare
 54+ * @param b {Object} Second object to compare
 55+ * @param asymmetrical {Boolean} Whether to check only that b contains values from a
 56+ * @returns {Boolean} If the objects contain the same values as each other
 57+ */
 58+es.compareObjects = function( a, b, asymmetrical ) {
 59+ var aValue, bValue, aType, bType;
 60+ var k;
 61+ for ( k in a ) {
 62+ aValue = a[k];
 63+ bValue = b[k];
 64+ aType = typeof aValue;
 65+ bType = typeof bValue;
 66+ if ( aType !== bType
 67+ || ( ( aType === 'string' || aType === 'number' ) && aValue !== bValue )
 68+ || ( $.isPlainObject( aValue ) && !es.compareObjects( aValue, bValue ) ) ) {
 69+ return false;
 70+ }
 71+ }
 72+ // If the check is not asymmetrical, recursing with the arguments swapped will verify our result
 73+ return asymmetrical ? true : es.compareObjects( b, a, true );
 74+};
 75+
 76+/**
 77+ * Gets a recursive copy of an object's string, number and plain-object property.
 78+ *
 79+ * @static
 80+ * @method
 81+ * @param source {Object} Object to copy
 82+ * @returns {Object} Copy of source object
 83+ */
 84+es.copyObject = function( source ) {
 85+ var destination = {};
 86+ var key;
 87+ for ( key in source ) {
 88+ sourceValue = source[key];
 89+ sourceType = typeof sourceValue;
 90+ if ( sourceType === 'string' || sourceType === 'number' ) {
 91+ destination[key] = sourceValue;
 92+ } else if ( $.isPlainObject( sourceValue ) ) {
 93+ destination[key] = es.copyObject( sourceValue );
 94+ }
 95+ }
 96+ return destination;
 97+};
Index: trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js
@@ -0,0 +1,125 @@
 2+/**
 3+ * Creates an es.ModelItem object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.EventEmitter}
 8+ * @property {es.ModelList} list Reference to list this item is in
 9+ */
 10+es.ModelItem = function() {
 11+ es.EventEmitter.call( this );
 12+ this.list = null;
 13+};
 14+
 15+/* Methods */
 16+
 17+/**
 18+ * Creates a view for this model.
 19+ *
 20+ * @method
 21+ * @returns {es.ViewItem} New item view associated with this item model
 22+ */
 23+es.ModelItem.prototype.createView = function() {
 24+ throw 'ModelItem.createView not implemented in this subclass:' + this.constructor;
 25+};
 26+
 27+/**
 28+ * Gets a reference to the list this item is in.
 29+ *
 30+ * @method
 31+ * @returns {es.ModelList} Reference to this list this item is in
 32+ */
 33+es.ModelItem.prototype.getList = function() {
 34+ return this.list;
 35+};
 36+
 37+/**
 38+ * Gets the index of this item within the list that it's in.
 39+ *
 40+ * @method
 41+ * @returns {Integer} Index of item in it's list, -1 if not in a list
 42+ */
 43+es.ModelItem.prototype.getIndex = function() {
 44+ if ( this.list ) {
 45+ return this.list.indexOf( this );
 46+ }
 47+ return -1;
 48+};
 49+
 50+/**
 51+ * Attaches item to a list.
 52+ *
 53+ * @method
 54+ * @param {es.Container} list Container to attach to
 55+ * @emits attach (list)
 56+ */
 57+es.ModelItem.prototype.attach = function( list ) {
 58+ this.list = list;
 59+ this.emit( 'attach', list );
 60+};
 61+
 62+/**
 63+ * Detaches item from a list.
 64+ *
 65+ * @method
 66+ * @emits detach (list)
 67+ */
 68+es.ModelItem.prototype.detach = function() {
 69+ var list = this.list;
 70+ this.list = null;
 71+ this.emit( 'detach', list );
 72+};
 73+
 74+/**
 75+ * Gets the previous item in list.
 76+ *
 77+ * @method
 78+ * @returns {es.ModelItem} Previous item, or undefined if none exists
 79+ */
 80+es.ModelItem.prototype.getPreviousItem = function() {
 81+ if ( this.list ) {
 82+ return this.list[this.list.indexOf( this ) - 1];
 83+ }
 84+};
 85+
 86+/**
 87+ * Gets the next item in list.
 88+ *
 89+ * @method
 90+ * @returns {Object} Next item, or undefined if none exists
 91+ */
 92+es.ModelItem.prototype.getNextItem = function() {
 93+ if ( this.list ) {
 94+ return this.list[this.list.indexOf( this ) + 1];
 95+ }
 96+};
 97+
 98+/**
 99+ * Checks if this item is the first in it's list.
 100+ *
 101+ * @method
 102+ * @returns {Boolean} If item is in a list and is also the first item in that list
 103+ */
 104+es.ModelItem.prototype.isFirstItem = function() {
 105+ if ( this.list ) {
 106+ return this === this.list.getFirstItem();
 107+ }
 108+ return false;
 109+};
 110+
 111+/**
 112+ * Checks if this item is the last in it's list.
 113+ *
 114+ * @method
 115+ * @returns {Boolean} If item is in a list and is also the last item in that list
 116+ */
 117+es.ModelItem.prototype.isLastItem = function() {
 118+ if ( this.list ) {
 119+ return this === this.list.getLastItem();
 120+ }
 121+ return false;
 122+};
 123+
 124+/* Inheritance */
 125+
 126+es.extend( es.ModelItem, es.EventEmitter );
Index: trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js
@@ -0,0 +1,117 @@
 2+/**
 3+ * Creates an es.AggregateArray object.
 4+ *
 5+ * A content series is an array of items which have a getLength method.
 6+ */
 7+es.AggregateArray = function( items ) {
 8+ var items = $.isArray( items ) ? items : [];
 9+ // Extend native array with method and properties of this
 10+ return $.extend( items, this );
 11+};
 12+
 13+es.AggregateArray.prototype.lookup = function( offset ) {
 14+ if ( this.length ) {
 15+ var i = 0,
 16+ length = this.length,
 17+ left = 0,
 18+ right;
 19+ while ( i < length ) {
 20+ right = left + this[i].getLength() + 1;
 21+ if ( offset >= left && offset < right ) {
 22+ return this[i];
 23+ }
 24+ left = right;
 25+ i++;
 26+ }
 27+ }
 28+ return null;
 29+};
 30+
 31+es.AggregateArray.prototype.rangeOf = function( item ) {
 32+ if ( this.length ) {
 33+ var i = 0,
 34+ length = this.length,
 35+ left = 0;
 36+ while ( i < length ) {
 37+ if ( this[i] === item ) {
 38+ return new es.Range( left, left + this[i].getLength() );
 39+ }
 40+ left += this[i].getLength() + 1;
 41+ i++;
 42+ }
 43+ }
 44+ return null;
 45+};
 46+
 47+es.AggregateArray.prototype.getLengthOfItems = function() {
 48+ var sum = 0;
 49+ for ( var i = 0, length = this.length; i < length; i++ ) {
 50+ sum += this[i].getLength();
 51+ }
 52+ return Math.max( 0, sum + this.length - 1 );
 53+};
 54+
 55+es.AggregateArray.prototype.getCoverage = function( start, end ) {
 56+ var result = { 'on': [], 'off': [] },
 57+ sum = 0,
 58+ len;
 59+ for ( var i = 0, length = this.length; i < length; i++ ) {
 60+ len = this[i].getLength();
 61+ if ( sum >= start && sum + len < end ) {
 62+ result.on.push( this[i] );
 63+ } else {
 64+ result.off.push( this[i] );
 65+ }
 66+ sum += len
 67+ }
 68+ return result;
 69+};
 70+
 71+es.AggregateArray.prototype.select = function( start, end ) {
 72+ // Support es.Range object as first argument
 73+ if ( typeof start.from === 'number' && typeof start.to === 'number') {
 74+ start.normalize();
 75+ end = start.end;
 76+ start = start.start;
 77+ }
 78+ var items = [];
 79+ if ( this.length ) {
 80+ var i = 0,
 81+ length = this.length,
 82+ left = 0,
 83+ right,
 84+ inside = false,
 85+ from,
 86+ to;
 87+ while ( i < length ) {
 88+ right = left + this[i].getLength() + 1;
 89+ if ( inside ) {
 90+ // Append items until we reach the end
 91+ from = 0;
 92+ to = Math.min( right - left - 1, end - left );
 93+
 94+ if ( from !== to ) {
 95+ items.push( { 'item': this[i], 'from': from, 'to': to } );
 96+ }
 97+ if ( end >= left && end < right ) {
 98+ break;
 99+ }
 100+ } else if ( start >= left && start < right ) {
 101+ inside = true;
 102+ // Append first item
 103+ from = start - left;
 104+ //to = Math.min( right - 1, end - left );
 105+ to = Math.min( right - left - 1, end - left );
 106+ if ( from !== to ) {
 107+ items.push( { 'item': this[i], 'from': from, 'to': to } );
 108+ }
 109+ if ( right >= end ) {
 110+ break;
 111+ }
 112+ }
 113+ left = right;
 114+ i++;
 115+ }
 116+ }
 117+ return items;
 118+};
Index: trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js
@@ -0,0 +1,132 @@
 2+/**
 3+ * Creates an es.ViewList object.
 4+ *
 5+ * View lists follow the operations performed on a model lists and keep a list of views,
 6+ * each correlating to a model in the model list.
 7+ *
 8+ * This will override this.$ (important in case of multiple inheritance).
 9+ *
 10+ * @class
 11+ * @constructor
 12+ * @extends {es.EventEmitter}
 13+ * @param model {es.ModelList} Model to observe
 14+ * @param {jQuery} [$element=New DIV element] Element to use as a container
 15+ * @property {es.ModelItem} model Model being observed
 16+ * @property {jQuery} $ Container element
 17+ */
 18+es.ViewList = function( model, $element ) {
 19+ var list = new es.AggregateArray();
 20+ es.EventEmitter.call( list );
 21+
 22+ // Extending this class will initialize it without any arguments, exiting early if no model
 23+ // was given will prevent clogging up subclass prototypes with array methods
 24+ if ( !model ) {
 25+ return list;
 26+ }
 27+
 28+ list.model = model;
 29+ list.$ = $element || $( '<div/>' );
 30+
 31+ // Reusable function for passing update events upstream
 32+ this.emitUpdate = function() {
 33+ list.emit( 'update' );
 34+ };
 35+
 36+ // Observe and mimic changes on model
 37+ model.addListenerMethods( list, {
 38+ 'push': 'onPush',
 39+ 'unshift': 'onUnshift',
 40+ 'pop': 'onPop',
 41+ 'shift': 'onShift',
 42+ 'splice': 'onSplice',
 43+ 'sort': 'onSort',
 44+ 'reverse': 'onReverse'
 45+ } );
 46+
 47+ // Append existing model items
 48+ for ( var i = 0; i < model.length; i++ ) {
 49+ this.onPush( model[i] );
 50+ }
 51+
 52+ // Extend native array with method and properties of this
 53+ return $.extend( list, this );
 54+};
 55+
 56+es.ViewList.onPush = function( itemModel ) {
 57+ var itemView = itemModel.createView();
 58+ itemView.attach( this );
 59+ itemView.on( 'update', this.emitUpdate );
 60+ this.push( itemView );
 61+ this.$.append( itemView.$ );
 62+ this.emit( 'push', itemView );
 63+ this.emit( 'update' );
 64+};
 65+
 66+es.ViewList.onUnshift = function( itemModel ) {
 67+ var itemView = itemModel.createView();
 68+ itemView.attach( this );
 69+ itemView.on( 'update', this.emitUpdate );
 70+ this.unshift( itemView );
 71+ this.$.prepend( itemView.$ );
 72+ this.emit( 'unshift', itemView );
 73+ this.emit( 'update' );
 74+};
 75+
 76+es.ViewList.onPop = function() {
 77+ var itemView = this.pop();
 78+ itemView.detach();
 79+ itemView.removeEventListener( 'update', this.emitUpdate );
 80+ itemView.$.detach();
 81+ this.emit( 'pop' );
 82+ this.emit( 'update' );
 83+};
 84+
 85+es.ViewList.onShift = function() {
 86+ var itemView = this.shift();
 87+ itemView.detach();
 88+ itemView.removeEventListener( 'update', this.emitUpdate );
 89+ itemView.$.detach();
 90+ this.emit( 'shift' );
 91+ this.emit( 'update' );
 92+};
 93+
 94+es.ViewList.onSplice = function( index, howmany ) {
 95+ var args = Array.prototype.slice( arguments, 0 ),
 96+ added = args.slice( 2 ),
 97+ removed = this.splice.apply( this, args );
 98+ this.$.children().slice( index, index + howmany ).detach();
 99+ var $added = $.map( added, function( itemView ) {
 100+ return itemView.$;
 101+ } );
 102+ this.$.children().get( index ).after( $added );
 103+ this.emit.apply( ['splice'].concat( args ) );
 104+ this.emit( 'update' );
 105+};
 106+
 107+es.ViewList.onSort = function() {
 108+ for ( var i = 0; i < this.model.length; i++ ) {
 109+ for ( var j = 0; j < this.length; j++ ) {
 110+ if ( this[j].getModel() === this.model[i] ) {
 111+ var itemView = this[j];
 112+ this.splice( j, 1 );
 113+ this.push( itemView );
 114+ this.$.append( itemView.$ );
 115+ }
 116+ }
 117+ }
 118+ this.emit( 'sort' );
 119+ this.emit( 'update' );
 120+};
 121+
 122+es.ViewList.onReverse = function() {
 123+ this.reverse();
 124+ this.$.children().each( function() {
 125+ $(this).prependTo( $(this).parent() );
 126+ } );
 127+ this.emit( 'reverse' );
 128+ this.emit( 'update' );
 129+};
 130+
 131+/* Inheritance */
 132+
 133+es.extend( es.ViewList, es.EventEmitter );
Index: trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js
@@ -0,0 +1,76 @@
 2+/**
 3+ * Generic synchronized Object/Element container item.
 4+ * This will override this.$ (important in case of multiple inheritance).
 5+ *
 6+ * @class
 7+ * @constructor
 8+ * @extends {es.EventEmitter}
 9+ * @param {jQuery} $element jQuery object to use
 10+ * @property {jQuery} $ Container element
 11+ */
 12+es.ViewItem = function( model, $element ) {
 13+ es.EventEmitter.call( this );
 14+ this.model = model;
 15+ this.$ = $element || $( '<div/>' );
 16+ this.list = null;
 17+};
 18+
 19+/**
 20+ * Gets a reference to the model this item observes.
 21+ *
 22+ * @method
 23+ * @returns {es.ModelList} Reference to the model this item observes
 24+ */
 25+es.ViewItem.prototype.getModel = function() {
 26+ return this.model;
 27+};
 28+
 29+/**
 30+ * Gets a reference to the list this item is in.
 31+ *
 32+ * @method
 33+ * @returns {es.ModelList} Reference to this list this item is in
 34+ */
 35+es.ViewItem.prototype.getList = function() {
 36+ return this.list;
 37+};
 38+
 39+/**
 40+ * Gets the index of this item within it's list.
 41+ *
 42+ * This method simply delegates to the model.
 43+ *
 44+ * @method
 45+ * @returns {Integer} Index of item in it's container
 46+ */
 47+es.ViewItem.prototype.getIndex = function() {
 48+ return this.model.getIndex();
 49+};
 50+
 51+/**
 52+ * Attaches item to a list.
 53+ *
 54+ * @method
 55+ * @param {es.Container} list Container to attach to
 56+ * @emits attach (list)
 57+ */
 58+es.ViewItem.prototype.attach = function( list ) {
 59+ this.list = list;
 60+ this.emit( 'attach', list );
 61+};
 62+
 63+/**
 64+ * Detaches item from a list.
 65+ *
 66+ * @method
 67+ * @emits detach (list)
 68+ */
 69+es.ViewItem.prototype.detach = function() {
 70+ var list = this.list;
 71+ this.list = null;
 72+ this.emit( 'detach', list );
 73+};
 74+
 75+/* Inheritance */
 76+
 77+es.extend( es.ViewItem, es.EventEmitter );
\ No newline at end of file
Index: trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js
@@ -0,0 +1,168 @@
 2+/**
 3+ * Event emitter.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @property events {Object}
 8+ */
 9+es.EventEmitter = function() {
 10+ this.events = {};
 11+}
 12+
 13+/* Methods */
 14+
 15+/**
 16+ * Emits an event.
 17+ *
 18+ * @method
 19+ * @param type {String} Type of event
 20+ * @param args {Mixed} First in a list of variadic arguments passed to event handler (optional)
 21+ * @returns {Boolean} If event was handled by at least one listener
 22+ */
 23+es.EventEmitter.prototype.emit = function( type ) {
 24+ if ( type === 'error' && !( 'error' in this.events ) ) {
 25+ throw 'Missing error handler error.';
 26+ }
 27+ if ( !( type in this.events ) ) {
 28+ return false;
 29+ }
 30+ var listeners = this.events[type].slice();
 31+ var args = Array.prototype.slice.call( arguments, 1 );
 32+ for ( var i = 0; i < listeners.length; i++ ) {
 33+ listeners[i].apply( this, args );
 34+ }
 35+ return true;
 36+};
 37+
 38+/**
 39+ * Adds a listener to events of a specific type.
 40+ *
 41+ * @method
 42+ * @param type {String} Type of event to listen to
 43+ * @param listener {Function} Listener to call when event occurs
 44+ * @returns {es.EventEmitter} This object
 45+ * @throws "Invalid listener error" if listener argument is not a function
 46+ */
 47+es.EventEmitter.prototype.addListener = function( type, listener ) {
 48+ if ( typeof listener !== 'function' ) {
 49+ throw 'Invalid listener error. Function expected.';
 50+ }
 51+ this.emit( 'newListener', type, listener );
 52+ if ( type in this.events ) {
 53+ this.events[type].push( listener );
 54+ } else {
 55+ this.events[type] = [listener];
 56+ }
 57+ return this;
 58+};
 59+
 60+/**
 61+ * Add multiple listeners at once.
 62+ *
 63+ * @method
 64+ * @param listeners {Object} List of event/callback pairs
 65+ * @returns {es.EventEmitter} This object
 66+ */
 67+es.EventEmitter.prototype.addListeners = function( listeners ) {
 68+ for ( var event in listeners ) {
 69+ this.addListener( event, listeners[event] );
 70+ }
 71+ return this;
 72+};
 73+
 74+/**
 75+ * Add multiple listeners, each mapped to a method on a target object.
 76+ *
 77+ * @method
 78+ * @param target {Object} Object to call methods on when events occur
 79+ * @param map {Object} List of event/method name pairs
 80+ * @returns {es.EventEmitter} This object
 81+ */
 82+es.EventEmitter.prototype.addListenerMethods = function( target, map ) {
 83+ for ( var event in map ) {
 84+ this.addListener( event, function() {
 85+ target[map[event]].apply( target, Array.prototype.slice( arguments, 1 ) ) );
 86+ } );
 87+ }
 88+ return this;
 89+};
 90+
 91+/**
 92+ * Alias for addListener
 93+ *
 94+ * @method
 95+ */
 96+es.EventEmitter.prototype.on = es.EventEmitter.prototype.addListener;
 97+
 98+/**
 99+ * Adds a one-time listener to a specific event.
 100+ *
 101+ * @method
 102+ * @param type {String} Type of event to listen to
 103+ * @param listener {Function} Listener to call when event occurs
 104+ * @returns {es.EventEmitter} This object
 105+ */
 106+es.EventEmitter.prototype.once = function( type, listener ) {
 107+ var eventEmitter = this;
 108+ return this.addListener( type, function listenerWrapper() {
 109+ eventEmitter.removeListener( type, listenerWrapper );
 110+ listener.apply( eventEmitter, arguments );
 111+ } );
 112+};
 113+
 114+/**
 115+ * Removes a specific listener from a specific event.
 116+ *
 117+ * @method
 118+ * @param type {String} Type of event to remove listener from
 119+ * @param listener {Function} Listener to remove
 120+ * @returns {es.EventEmitter} This object
 121+ * @throws "Invalid listener error" if listener argument is not a function
 122+ */
 123+es.EventEmitter.prototype.removeListener = function( type, listener ) {
 124+ if ( typeof listener !== 'function' ) {
 125+ throw 'Invalid listener error. Function expected.';
 126+ }
 127+ if ( !( type in this.events ) || !this.events[type].length ) {
 128+ return this;
 129+ }
 130+ var handlers = this.events[type];
 131+ if ( handlers.length == 1 && handlers[0] === listener ) {
 132+ delete this.events[type];
 133+ } else {
 134+ var i = handlers.indexOf( listener );
 135+ if ( i < 0 ) {
 136+ return this;
 137+ }
 138+ handlers.splice( i, 1 );
 139+ if ( handlers.length == 0 ) {
 140+ delete this.events[type];
 141+ }
 142+ }
 143+ return this;
 144+};
 145+
 146+/**
 147+ * Removes all listeners from a specific event.
 148+ *
 149+ * @method
 150+ * @param type {String} Type of event to remove listeners from
 151+ * @returns {es.EventEmitter} This object
 152+ */
 153+es.EventEmitter.prototype.removeAllListeners = function( type ) {
 154+ if ( type in this.events ) {
 155+ delete this.events[type];
 156+ }
 157+ return this;
 158+};
 159+
 160+/**
 161+ * Gets a list of listeners attached to a specific event.
 162+ *
 163+ * @method
 164+ * @param type {String} Type of event to get listeners for
 165+ * @returns {Array} List of listeners to an event
 166+ */
 167+es.EventEmitter.prototype.listeners = function( type ) {
 168+ return type in this.events ? this.events[type] : [];
 169+};
Index: trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js
@@ -0,0 +1,182 @@
 2+/**
 3+ * Creates an es.ModelList object.
 4+ *
 5+ * es.ModelList extends native JavaScript Array objects, without changing Array.prototype by
 6+ * dynamically extending an array literal with the methods of es.ModelList.
 7+ *
 8+ * Child objects must extend es.ModelItem.
 9+ *
 10+ * @class
 11+ * @constructor
 12+ * @extends {Array}
 13+ * @extends {es.EventEmitter}
 14+ */
 15+es.ModelList = function( items ) {
 16+ var items = new AggregateArray( items );
 17+ es.EventEmitter.call( items );
 18+
 19+ // Reusable function for passing update events upstream
 20+ items.emitUpdate = function() {
 21+ items.emit( 'update' );
 22+ };
 23+
 24+ // Extend native array with method and properties of this
 25+ return $.extend( items, this );
 26+};
 27+
 28+/* Methods */
 29+
 30+/**
 31+ * Gets the first item in the list.
 32+ *
 33+ * @method
 34+ * @returns {es.ModelItem|undefined} First item in the list, or undefined if none exists
 35+ */
 36+es.ModelList.prototype.getFirstItem = function() {
 37+ return this[0];
 38+};
 39+
 40+/**
 41+ * Gets the last item in the list.
 42+ *
 43+ * @method
 44+ * @returns {es.ModelItem|undefined} last item in the list, or undefined if none exists
 45+ */
 46+es.ModelList.prototype.getLastItem = function() {
 47+ return this[this.length - 1];
 48+};
 49+
 50+/**
 51+ * Adds an item to the end of the list.
 52+ *
 53+ * @method
 54+ * @param {es.ModelItem} item Item to add
 55+ * @returns {Integer} New length of list
 56+ * @emits push (item)
 57+ * @emits update
 58+ */
 59+es.ModelList.prototype.push = function( item ) {
 60+ item.attach( this );
 61+ item.on( 'update', this.relayUpdate );
 62+ Array.prototype.push.call( this, item );
 63+ this.emit( 'push', item );
 64+ this.emit( 'update' );
 65+ return this.length;
 66+};
 67+
 68+/**
 69+ * Adds an item to the beginning of the list.
 70+ *
 71+ * @method
 72+ * @param {es.ModelItem} item Item to add
 73+ * @returns {Integer} New length of list
 74+ * @emits unshift (item)
 75+ * @emits update
 76+ */
 77+es.ModelList.prototype.unshift = function( item ) {
 78+ item.attach( this );
 79+ item.on( 'update', this.relayUpdate );
 80+ Array.prototype.unshift.call( this, item );
 81+ this.emit( 'unshift', item );
 82+ this.emit( 'update' );
 83+ return this.length;
 84+};
 85+
 86+/**
 87+ * Removes an item from the end of the list.
 88+ *
 89+ * @method
 90+ * @returns {Integer} Removed item
 91+ * @emits pop
 92+ * @emits update
 93+ */
 94+es.ModelList.prototype.pop = function() {
 95+ if ( this.length ) {
 96+ var item = this[this.length - 1];
 97+ item.detach();
 98+ item.removeListener( 'update', this.relayUpdate );
 99+ Array.prototype.pop.call( this, item );
 100+ this.emit( 'pop' );
 101+ this.emit( 'update' );
 102+ return item;
 103+ }
 104+};
 105+
 106+/**
 107+ * Removes an item from the beginning of the list.
 108+ *
 109+ * @method
 110+ * @returns {Integer} Removed item
 111+ * @emits shift
 112+ * @emits update
 113+ */
 114+es.ModelList.prototype.shift = function() {
 115+ if ( this.length ) {
 116+ var item = this[0];
 117+ item.detach();
 118+ item.removeListener( 'update', this.relayUpdate );
 119+ Array.prototype.shift.call( this, item );
 120+ this.emit( 'shift' );
 121+ this.emit( 'update' );
 122+ return item;
 123+ }
 124+};
 125+
 126+/**
 127+ * Removes an item from the beginning of the list.
 128+ *
 129+ * @method
 130+ * @param {Integer} index Index to remove and or insert items
 131+ * @param {Integer} howmany Number of items to remove
 132+ * @param {es.ModelItem} [...] Variadic list of items to insert
 133+ * @returns {es.ModelItem[]} Removed items
 134+ * @emits splice (index, howmany, [...])
 135+ * @emits update
 136+ */
 137+es.ModelList.prototype.splice = function( index, howmany ) {
 138+ var args = Array.prototype.slice.call( arguments, 0 );
 139+ if ( args.length >= 3 ) {
 140+ for ( var i = 2; i < args.length; i++ ) {
 141+ args[i].attach( this );
 142+ }
 143+ }
 144+ var removed = Array.prototype.splice.apply( this, args );
 145+ for ( var i = 0; i < removed.length; i++ ) {
 146+ removed[i].detach();
 147+ removed[i].removeListener( 'update', this.relayUpdate );
 148+ }
 149+ this.emit.apply( ['splice'].concat( args ) );
 150+ this.emit( 'update' );
 151+ return removed;
 152+};
 153+
 154+/**
 155+ * Sorts items in list.
 156+ *
 157+ * @method
 158+ * @param {Function} sortfunc Function to use when sorting
 159+ * @emits sort
 160+ * @emits update
 161+ */
 162+es.ModelList.prototype.sort = function( sortfunc ) {
 163+ this.emit( 'sort' );
 164+ this.emit( 'update' );
 165+ Array.prototype.reverse.call( this );
 166+};
 167+
 168+/**
 169+ * Reverses the order of the list.
 170+ *
 171+ * @method
 172+ * @emits reverse
 173+ * @emits update
 174+ */
 175+es.ModelList.prototype.reverse = function() {
 176+ this.emit( 'reverse' );
 177+ this.emit( 'update' );
 178+ Array.prototype.reverse.call( this );
 179+};
 180+
 181+/* Inheritance */
 182+
 183+es.extend( es.ModelList, es.EventEmitter );

Status & tagging log