http://www.mediawiki.org/wiki/Special:Code/MediaWiki/97977

Revision: 97977
Author:   tparscal
Date:     2011-09-24 00:14:53 +0000 (Sat, 24 Sep 2011)
Log Message:
-----------
Added hype code, this is just temporary while we sort out how this will work, 
then we will merge it into synth

Added Paths:
-----------
    trunk/parsers/wikidom/lib/hype/
    trunk/parsers/wikidom/lib/hype/bases/
    trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js
    trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js
    trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js
    trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js
    trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js
    trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js
    trunk/parsers/wikidom/lib/hype/es.DocumentModel.js
    trunk/parsers/wikidom/lib/hype/es.js
    trunk/parsers/wikidom/lib/hype/models/

Added: trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js                   
        (rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.AggregateArray.js   2011-09-24 
00:14:53 UTC (rev 97977)
@@ -0,0 +1,117 @@
+/**
+ * Creates an es.AggregateArray object.
+ * 
+ * A content series is an array of items which have a getLength method. 
+ */
+es.AggregateArray = function( items ) {
+       var items = $.isArray( items ) ? items : [];
+       // Extend native array with method and properties of this
+       return $.extend( items, this );
+};
+
+es.AggregateArray.prototype.lookup = function( offset ) {
+       if ( this.length ) {
+               var i = 0,
+                       length = this.length,
+                       left = 0,
+                       right;
+               while ( i < length ) {
+                       right = left + this[i].getLength() + 1;
+                       if ( offset >= left && offset < right ) {
+                               return this[i];
+                       }
+                       left = right;
+                       i++;
+               }
+       }
+       return null;
+};
+
+es.AggregateArray.prototype.rangeOf = function( item ) {
+       if ( this.length ) {
+               var i = 0,
+                       length = this.length,
+                       left = 0;
+               while ( i < length ) {
+                       if ( this[i] === item ) {
+                               return new es.Range( left, left + 
this[i].getLength() );
+                       }
+                       left += this[i].getLength() + 1;
+                       i++;
+               }
+       }
+       return null;
+};
+
+es.AggregateArray.prototype.getLengthOfItems = function() {
+       var sum = 0;
+       for ( var i = 0, length = this.length; i < length; i++ ) {
+               sum += this[i].getLength();
+       }
+       return Math.max( 0, sum + this.length - 1 );
+};
+
+es.AggregateArray.prototype.getCoverage = function( start, end ) {
+       var result = { 'on': [], 'off': [] },
+               sum = 0,
+               len;
+       for ( var i = 0, length = this.length; i < length; i++ ) {
+               len = this[i].getLength();
+               if ( sum >= start && sum + len < end ) {
+                       result.on.push( this[i] );
+               } else {
+                       result.off.push( this[i] );
+               }
+               sum += len
+       }
+       return result;
+};
+
+es.AggregateArray.prototype.select = function( start, end ) {
+       // Support es.Range object as first argument
+       if ( typeof start.from === 'number' && typeof start.to === 'number') {
+               start.normalize();
+               end = start.end;
+               start = start.start;
+       }
+       var items = [];
+       if ( this.length ) {
+               var i = 0,
+                       length = this.length,
+                       left = 0,
+                       right,
+                       inside = false,
+                       from,
+                       to;
+               while ( i < length ) {
+                       right = left + this[i].getLength() + 1;
+                       if ( inside ) {
+                               // Append items until we reach the end
+                               from = 0;
+                               to = Math.min( right - left - 1, end - left );
+
+                               if ( from !== to ) {
+                                       items.push( { 'item': this[i], 'from': 
from, 'to': to } );
+                               }
+                               if ( end >= left && end < right ) {
+                                       break;
+                               }
+                       } else if ( start >= left && start < right ) {
+                               inside = true;
+                               // Append first item
+                               from = start - left;
+                               //to = Math.min( right - 1, end - left );
+                               to = Math.min( right - left - 1, end - left );
+                               if ( from !== to ) {
+                                       items.push( { 'item': this[i], 'from': 
from, 'to': to } );
+                               }
+                               if ( right >= end ) {
+                                       break;
+                               }
+                       }
+                       left = right;
+                       i++;
+               }
+       }
+       return items;
+};

Added: trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js                     
        (rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.EventEmitter.js     2011-09-24 
00:14:53 UTC (rev 97977)
@@ -0,0 +1,168 @@
+/**
+ * Event emitter.
+ * 
+ * @class
+ * @constructor
+ * @property events {Object}
+ */
+es.EventEmitter = function() {
+       this.events = {};
+}
+
+/* Methods */
+
+/**
+ * Emits an event.
+ * 
+ * @method
+ * @param type {String} Type of event
+ * @param args {Mixed} First in a list of variadic arguments passed to event 
handler (optional)
+ * @returns {Boolean} If event was handled by at least one listener
+ */
+es.EventEmitter.prototype.emit = function( type ) {
+       if ( type === 'error' && !( 'error' in this.events ) ) {
+               throw 'Missing error handler error.';
+       }
+       if ( !( type in this.events ) ) {
+               return false;
+       }
+       var listeners = this.events[type].slice();
+       var args = Array.prototype.slice.call( arguments, 1 );
+       for ( var i = 0; i < listeners.length; i++ ) {
+               listeners[i].apply( this, args );
+       }
+       return true;
+};
+
+/**
+ * Adds a listener to events of a specific type.
+ * 
+ * @method
+ * @param type {String} Type of event to listen to
+ * @param listener {Function} Listener to call when event occurs
+ * @returns {es.EventEmitter} This object
+ * @throws "Invalid listener error" if listener argument is not a function
+ */
+es.EventEmitter.prototype.addListener = function( type, listener ) {
+       if ( typeof listener !== 'function' ) {
+               throw 'Invalid listener error. Function expected.';
+       }
+       this.emit( 'newListener', type, listener );
+       if ( type in this.events ) {
+               this.events[type].push( listener );
+       } else {
+               this.events[type] = [listener];
+       }
+       return this;
+};
+
+/**
+ * Add multiple listeners at once.
+ * 
+ * @method
+ * @param listeners {Object} List of event/callback pairs
+ * @returns {es.EventEmitter} This object
+ */
+es.EventEmitter.prototype.addListeners = function( listeners ) {
+       for ( var event in listeners ) {
+               this.addListener( event, listeners[event] );
+       }
+       return this;
+};
+
+/**
+ * Add multiple listeners, each mapped to a method on a target object.
+ * 
+ * @method
+ * @param target {Object} Object to call methods on when events occur
+ * @param map {Object} List of event/method name pairs
+ * @returns {es.EventEmitter} This object
+ */
+es.EventEmitter.prototype.addListenerMethods = function( target, map ) {
+       for ( var event in map ) {
+               this.addListener( event, function() {
+                       target[map[event]].apply( target, 
Array.prototype.slice( arguments, 1 ) ) );
+               } );
+       }
+       return this;
+};
+
+/**
+ * Alias for addListener
+ * 
+ * @method
+ */
+es.EventEmitter.prototype.on = es.EventEmitter.prototype.addListener;
+
+/**
+ * Adds a one-time listener to a specific event.
+ * 
+ * @method
+ * @param type {String} Type of event to listen to
+ * @param listener {Function} Listener to call when event occurs
+ * @returns {es.EventEmitter} This object
+ */
+es.EventEmitter.prototype.once = function( type, listener ) {
+       var eventEmitter = this;
+       return this.addListener( type, function listenerWrapper() {
+               eventEmitter.removeListener( type, listenerWrapper );
+               listener.apply( eventEmitter, arguments );
+       } );
+};
+
+/**
+ * Removes a specific listener from a specific event.
+ * 
+ * @method
+ * @param type {String} Type of event to remove listener from
+ * @param listener {Function} Listener to remove
+ * @returns {es.EventEmitter} This object
+ * @throws "Invalid listener error" if listener argument is not a function
+ */
+es.EventEmitter.prototype.removeListener = function( type, listener ) {
+       if ( typeof listener !== 'function' ) {
+               throw 'Invalid listener error. Function expected.';
+       }
+       if ( !( type in this.events ) || !this.events[type].length ) {
+               return this;
+       }
+       var handlers = this.events[type];
+       if ( handlers.length == 1 && handlers[0] === listener ) {
+               delete this.events[type];
+       } else {
+               var i = handlers.indexOf( listener );
+               if ( i < 0 ) {
+                       return this;
+               }
+               handlers.splice( i, 1 );
+               if ( handlers.length == 0 ) {
+                       delete this.events[type];
+               }
+       }
+       return this;
+};
+
+/**
+ * Removes all listeners from a specific event.
+ * 
+ * @method
+ * @param type {String} Type of event to remove listeners from
+ * @returns {es.EventEmitter} This object
+ */
+es.EventEmitter.prototype.removeAllListeners = function( type ) {
+       if ( type in this.events ) {
+               delete this.events[type];
+       }
+       return this;
+};
+
+/**
+ * Gets a list of listeners attached to a specific event.
+ * 
+ * @method
+ * @param type {String} Type of event to get listeners for
+ * @returns {Array} List of listeners to an event
+ */
+es.EventEmitter.prototype.listeners = function( type ) {
+       return type in this.events ? this.events[type] : [];
+};

Added: trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js                        
        (rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.ModelItem.js        2011-09-24 
00:14:53 UTC (rev 97977)
@@ -0,0 +1,125 @@
+/**
+ * Creates an es.ModelItem object.
+ * 
+ * @class
+ * @constructor
+ * @extends {es.EventEmitter}
+ * @property {es.ModelList} list Reference to list this item is in
+ */
+es.ModelItem = function() {
+       es.EventEmitter.call( this );
+       this.list = null;
+};
+
+/* Methods */
+
+/**
+ * Creates a view for this model.
+ * 
+ * @method
+ * @returns {es.ViewItem} New item view associated with this item model
+ */
+es.ModelItem.prototype.createView = function() {
+       throw 'ModelItem.createView not implemented in this subclass:' + 
this.constructor;
+};
+
+/**
+ * Gets a reference to the list this item is in.
+ * 
+ * @method
+ * @returns {es.ModelList} Reference to this list this item is in
+ */
+es.ModelItem.prototype.getList = function() {
+       return this.list;
+};
+
+/**
+ * Gets the index of this item within the list that it's in.
+ * 
+ * @method
+ * @returns {Integer} Index of item in it's list, -1 if not in a list
+ */
+es.ModelItem.prototype.getIndex = function() {
+       if ( this.list ) {
+               return this.list.indexOf( this );
+       }
+       return -1;
+};
+
+/**
+ * Attaches item to a list.
+ * 
+ * @method
+ * @param {es.Container} list Container to attach to
+ * @emits attach (list)
+ */
+es.ModelItem.prototype.attach = function( list ) {
+       this.list = list;
+       this.emit( 'attach', list );
+};
+
+/**
+ * Detaches item from a list.
+ * 
+ * @method
+ * @emits detach (list)
+ */
+es.ModelItem.prototype.detach = function() {
+       var list = this.list;
+       this.list = null;
+       this.emit( 'detach', list );
+};
+
+/**
+ * Gets the previous item in list.
+ * 
+ * @method
+ * @returns {es.ModelItem} Previous item, or undefined if none exists
+ */
+es.ModelItem.prototype.getPreviousItem = function() {
+       if ( this.list ) {
+               return this.list[this.list.indexOf( this ) - 1];
+       }
+};
+
+/**
+ * Gets the next item in list.
+ * 
+ * @method
+ * @returns {Object} Next item, or undefined if none exists
+ */
+es.ModelItem.prototype.getNextItem = function() {
+       if ( this.list ) {
+               return this.list[this.list.indexOf( this ) + 1];
+       }
+};
+
+/**
+ * Checks if this item is the first in it's list.
+ * 
+ * @method
+ * @returns {Boolean} If item is in a list and is also the first item in that 
list
+ */
+es.ModelItem.prototype.isFirstItem = function() {
+       if ( this.list ) {
+               return this === this.list.getFirstItem();
+       }
+       return false;
+};
+
+/**
+ * Checks if this item is the last in it's list.
+ * 
+ * @method
+ * @returns {Boolean} If item is in a list and is also the last item in that 
list
+ */
+es.ModelItem.prototype.isLastItem = function() {
+       if ( this.list ) {
+               return this === this.list.getLastItem();
+       }
+       return false;
+};
+
+/* Inheritance */
+
+es.extend( es.ModelItem, es.EventEmitter );

Added: trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js                        
        (rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.ModelList.js        2011-09-24 
00:14:53 UTC (rev 97977)
@@ -0,0 +1,182 @@
+/**
+ * Creates an es.ModelList object.
+ * 
+ * es.ModelList extends native JavaScript Array objects, without changing 
Array.prototype by
+ * dynamically extending an array literal with the methods of es.ModelList.
+ * 
+ * Child objects must extend es.ModelItem.
+ * 
+ * @class
+ * @constructor
+ * @extends {Array}
+ * @extends {es.EventEmitter}
+ */
+es.ModelList = function( items ) {
+       var items = new AggregateArray( items );
+       es.EventEmitter.call( items );
+       
+       // Reusable function for passing update events upstream
+       items.emitUpdate = function() {
+               items.emit( 'update' );
+       };
+       
+       // Extend native array with method and properties of this
+       return $.extend( items, this );
+};
+
+/* Methods */
+
+/**
+ * Gets the first item in the list.
+ * 
+ * @method
+ * @returns {es.ModelItem|undefined} First item in the list, or undefined if 
none exists
+ */
+es.ModelList.prototype.getFirstItem = function() {
+       return this[0];
+};
+
+/**
+ * Gets the last item in the list.
+ * 
+ * @method
+ * @returns {es.ModelItem|undefined} last item in the list, or undefined if 
none exists
+ */
+es.ModelList.prototype.getLastItem = function() {
+       return this[this.length - 1];
+};
+
+/**
+ * Adds an item to the end of the list.
+ * 
+ * @method
+ * @param {es.ModelItem} item Item to add
+ * @returns {Integer} New length of list
+ * @emits push (item)
+ * @emits update
+ */
+es.ModelList.prototype.push = function( item ) {
+       item.attach( this );
+       item.on( 'update', this.relayUpdate );
+       Array.prototype.push.call( this, item );
+       this.emit( 'push', item );
+       this.emit( 'update' );
+       return this.length;
+};
+
+/**
+ * Adds an item to the beginning of the list.
+ * 
+ * @method
+ * @param {es.ModelItem} item Item to add
+ * @returns {Integer} New length of list
+ * @emits unshift (item)
+ * @emits update
+ */
+es.ModelList.prototype.unshift = function( item ) {
+       item.attach( this );
+       item.on( 'update', this.relayUpdate );
+       Array.prototype.unshift.call( this, item );
+       this.emit( 'unshift', item );
+       this.emit( 'update' );
+       return this.length;
+};
+
+/**
+ * Removes an item from the end of the list.
+ * 
+ * @method
+ * @returns {Integer} Removed item
+ * @emits pop
+ * @emits update
+ */
+es.ModelList.prototype.pop = function() {
+       if ( this.length ) {
+               var item = this[this.length - 1];
+               item.detach();
+               item.removeListener( 'update', this.relayUpdate );
+               Array.prototype.pop.call( this, item );
+               this.emit( 'pop' );
+               this.emit( 'update' );
+               return item;
+       }
+};
+
+/**
+ * Removes an item from the beginning of the list.
+ * 
+ * @method
+ * @returns {Integer} Removed item
+ * @emits shift
+ * @emits update
+ */
+es.ModelList.prototype.shift = function() {
+       if ( this.length ) {
+               var item = this[0];
+               item.detach();
+               item.removeListener( 'update', this.relayUpdate );
+               Array.prototype.shift.call( this, item );
+               this.emit( 'shift' );
+               this.emit( 'update' );
+               return item;
+       }
+};
+
+/**
+ * Removes an item from the beginning of the list.
+ * 
+ * @method
+ * @param {Integer} index Index to remove and or insert items
+ * @param {Integer} howmany Number of items to remove
+ * @param {es.ModelItem} [...] Variadic list of items to insert
+ * @returns {es.ModelItem[]} Removed items
+ * @emits splice (index, howmany, [...])
+ * @emits update
+ */
+es.ModelList.prototype.splice = function( index, howmany ) {
+       var args = Array.prototype.slice.call( arguments, 0 );
+       if ( args.length >= 3 ) {
+               for ( var i = 2; i < args.length; i++ ) {
+                       args[i].attach( this );
+               }
+       }
+       var removed = Array.prototype.splice.apply( this, args );
+       for ( var i = 0; i < removed.length; i++ ) {
+               removed[i].detach();
+               removed[i].removeListener( 'update', this.relayUpdate );
+       }
+       this.emit.apply( ['splice'].concat( args ) );
+       this.emit( 'update' );
+       return removed;
+};
+
+/**
+ * Sorts items in list.
+ * 
+ * @method
+ * @param {Function} sortfunc Function to use when sorting
+ * @emits sort
+ * @emits update
+ */
+es.ModelList.prototype.sort = function( sortfunc ) {
+       this.emit( 'sort' );
+       this.emit( 'update' );
+       Array.prototype.reverse.call( this );
+};
+
+/**
+ * Reverses the order of the list.
+ * 
+ * @method
+ * @emits reverse
+ * @emits update
+ */
+es.ModelList.prototype.reverse = function() {
+       this.emit( 'reverse' );
+       this.emit( 'update' );
+       Array.prototype.reverse.call( this );
+};
+
+/* Inheritance */
+
+es.extend( es.ModelList, es.EventEmitter );

Added: trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js                         
(rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.ViewItem.js 2011-09-24 00:14:53 UTC 
(rev 97977)
@@ -0,0 +1,76 @@
+/**
+ * Generic synchronized Object/Element container item.
+ * This will override this.$ (important in case of multiple inheritance).
+ * 
+ * @class
+ * @constructor
+ * @extends {es.EventEmitter}
+ * @param {jQuery} $element jQuery object to use
+ * @property {jQuery} $ Container element
+ */
+es.ViewItem = function( model, $element ) {
+       es.EventEmitter.call( this );
+       this.model = model;
+       this.$ = $element || $( '<div/>' );
+       this.list = null;
+};
+
+/**
+ * Gets a reference to the model this item observes.
+ * 
+ * @method
+ * @returns {es.ModelList} Reference to the model this item observes
+ */
+es.ViewItem.prototype.getModel = function() {
+       return this.model;
+};
+
+/**
+ * Gets a reference to the list this item is in.
+ * 
+ * @method
+ * @returns {es.ModelList} Reference to this list this item is in
+ */
+es.ViewItem.prototype.getList = function() {
+       return this.list;
+};
+
+/**
+ * Gets the index of this item within it's list.
+ * 
+ * This method simply delegates to the model.
+ * 
+ * @method
+ * @returns {Integer} Index of item in it's container
+ */
+es.ViewItem.prototype.getIndex = function() {
+       return this.model.getIndex();
+};
+
+/**
+ * Attaches item to a list.
+ * 
+ * @method
+ * @param {es.Container} list Container to attach to
+ * @emits attach (list)
+ */
+es.ViewItem.prototype.attach = function( list ) {
+       this.list = list;
+       this.emit( 'attach', list );
+};
+
+/**
+ * Detaches item from a list.
+ * 
+ * @method
+ * @emits detach (list)
+ */
+es.ViewItem.prototype.detach = function() {
+       var list = this.list;
+       this.list = null;
+       this.emit( 'detach', list );
+};
+
+/* Inheritance */
+
+es.extend( es.ViewItem, es.EventEmitter );
\ No newline at end of file

Added: trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js                         
(rev 0)
+++ trunk/parsers/wikidom/lib/hype/bases/es.ViewList.js 2011-09-24 00:14:53 UTC 
(rev 97977)
@@ -0,0 +1,132 @@
+/**
+ * Creates an es.ViewList object.
+ * 
+ * View lists follow the operations performed on a model lists and keep a list 
of views,
+ * each correlating to a model in the model list.
+ *
+ * This will override this.$ (important in case of multiple inheritance).
+ * 
+ * @class
+ * @constructor
+ * @extends {es.EventEmitter}
+ * @param model {es.ModelList} Model to observe
+ * @param {jQuery} [$element=New DIV element] Element to use as a container
+ * @property {es.ModelItem} model Model being observed
+ * @property {jQuery} $ Container element
+ */
+es.ViewList = function( model, $element ) {
+       var list = new es.AggregateArray();
+       es.EventEmitter.call( list );
+       
+       // Extending this class will initialize it without any arguments, 
exiting early if no model
+       // was given will prevent clogging up subclass prototypes with array 
methods
+       if ( !model ) {
+               return list;
+       }
+       
+       list.model = model;
+       list.$ = $element || $( '<div/>' );
+       
+       // Reusable function for passing update events upstream
+       this.emitUpdate = function() {
+               list.emit( 'update' );
+       };
+       
+       // Observe and mimic changes on model
+       model.addListenerMethods( list, {
+               'push': 'onPush',
+               'unshift': 'onUnshift',
+               'pop': 'onPop',
+               'shift': 'onShift',
+               'splice': 'onSplice',
+               'sort': 'onSort',
+               'reverse': 'onReverse'
+       } );
+       
+       // Append existing model items
+       for ( var i = 0; i < model.length; i++ ) {
+               this.onPush( model[i] );
+       }
+       
+       // Extend native array with method and properties of this
+       return $.extend( list, this );
+};
+
+es.ViewList.onPush = function( itemModel ) {
+       var itemView = itemModel.createView();
+       itemView.attach( this );
+       itemView.on( 'update', this.emitUpdate );
+       this.push( itemView );
+       this.$.append( itemView.$ );
+       this.emit( 'push', itemView );
+       this.emit( 'update' );
+};
+
+es.ViewList.onUnshift = function( itemModel ) {
+       var itemView = itemModel.createView();
+       itemView.attach( this );
+       itemView.on( 'update', this.emitUpdate );
+       this.unshift( itemView );
+       this.$.prepend( itemView.$ );
+       this.emit( 'unshift', itemView );
+       this.emit( 'update' );
+};
+
+es.ViewList.onPop = function() {
+       var itemView = this.pop();
+       itemView.detach();
+       itemView.removeEventListener( 'update', this.emitUpdate );
+       itemView.$.detach();
+       this.emit( 'pop' );
+       this.emit( 'update' );
+};
+
+es.ViewList.onShift = function() {
+       var itemView = this.shift();
+       itemView.detach();
+       itemView.removeEventListener( 'update', this.emitUpdate );
+       itemView.$.detach();
+       this.emit( 'shift' );
+       this.emit( 'update' );
+};
+
+es.ViewList.onSplice = function( index, howmany ) {
+       var args = Array.prototype.slice( arguments, 0 ),
+               added = args.slice( 2 ),
+               removed = this.splice.apply( this, args );
+       this.$.children().slice( index, index + howmany ).detach();
+       var $added = $.map( added, function( itemView ) {
+               return itemView.$;
+       } );
+       this.$.children().get( index ).after( $added );
+       this.emit.apply( ['splice'].concat( args ) );
+       this.emit( 'update' );
+};
+
+es.ViewList.onSort = function() {
+       for ( var i = 0; i < this.model.length; i++ ) {
+               for ( var j = 0; j < this.length; j++ ) {
+                       if ( this[j].getModel() === this.model[i] ) {
+                               var itemView = this[j];
+                               this.splice( j, 1 );
+                               this.push( itemView );
+                               this.$.append( itemView.$ );
+                       }
+               }
+       }
+       this.emit( 'sort' );
+       this.emit( 'update' );
+};
+
+es.ViewList.onReverse = function() {
+       this.reverse();
+       this.$.children().each( function() {
+               $(this).prependTo( $(this).parent() );
+       } );
+       this.emit( 'reverse' );
+       this.emit( 'update' );
+};
+
+/* Inheritance */
+
+es.extend( es.ViewList, es.EventEmitter );

Added: trunk/parsers/wikidom/lib/hype/es.DocumentModel.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/es.DocumentModel.js                          
(rev 0)
+++ trunk/parsers/wikidom/lib/hype/es.DocumentModel.js  2011-09-24 00:14:53 UTC 
(rev 97977)
@@ -0,0 +1,223 @@
+/**
+ * @class
+ * @constructor
+ */
+es.DocumentModel = function() {
+       this.data = [];
+};
+
+/*
+// High-level operations
+
+es.DocumentModel.prototype.prepareInsertContent = function( offset, content ) {
+       // retain ^ .. offset
+       // insert content
+       // retain offset .. $
+};
+es.DocumentModel.prototype.prepareRemoveContent = function( range ) {
+       // retain ^ .. range.start
+       // retain range.end .. $
+};
+es.DocumentModel.prototype.prepareAnnotateContent = function( range, 
annotations ) {
+       // retain ^ .. range.start
+       // start annotations
+       // retain range.start .. range.end
+       // end annotations
+       // retain range.end .. $
+};
+es.DocumentModel.prototype.prepareInsertBlock = function( offset, type, 
attributes, content ) {
+       // retain ^ .. offset
+       // insert elementStart (type, attributes)
+       // insert content
+       // insert elementEnd (type, attributes)
+       // retain offset .. $
+};
+es.DocumentModel.prototype.prepareRemoveBlock = function( offset ) {
+       // retain ^ .. offset
+       // retain findMatchingElementEnd( offset ) .. $
+};
+*/
+
+// Low-level operations
+
+es.DocumentModel.newFromPlainObject = function( obj ) {
+       
+};
+
+es.DocumentModel.prototype.toPlainObject = function() {
+       
+};
+
+es.DocumentModel.prototype.insertContent = function( offset, content ) {
+       this.data = this.data.slice( 0, offset ).concat( content ).concat( 
this.data.slice( offset ) );
+};
+
+es.DocumentModel.prototype.removeContent = function( range ) {
+       this.data.splice( range.start, range.end - range.start );
+};
+
+es.DocumentModel.prototype.annotateContent = function( range, annotations ) {
+       for ( var i = 0; i < annotations.length; i++ ) {
+               var annotation = annotations[i];
+               if ( annotation.action = 'add' ) {
+                       // this.data[i][?]
+               } else if ( annotation.action = 'remove' ) {
+                       // this.data[i][?]
+               }
+       }
+};
+
+es.DocumentModel.prototype.insertElement = function( offset, element ) {
+       this.data.splice( offset, 0, element );
+};
+
+es.DocumentModel.prototype.removeElement = function( offset ) {
+       this.data.splice( offset, 1 );
+};
+
+es.DocumentModel.prototype.annotateElement = function( offset, annotations ) {
+       for ( var i = 0; i < annotations.length; i++ ) {
+               var annotation = annotations[i];
+               if ( annotation.action = 'add' ) {
+                       // this.data[i].annotations[?]
+               } else if ( annotation.action = 'remove' ) {
+                       // this.data[i].annotations[?]
+               }
+       }
+};
+
+/*
+ * Example of content data
+ * 
+ * Content data is an array made up of 3 kinds of values:
+ *             String: Plain text character
+ *             Array: Annotated character
+ *             Object: Opening or closing structural element
+ */
+var data = [
+       //  0 - Beginning of paragraph
+       { 'type': 'paragraph' },
+       //  1 - Plain content
+       'a',
+       //  2 - Annotated content
+       ['b', { 'type': 'bold' }],
+       //  3 - Annotated content
+       ['c', { 'type': 'italic' }],
+       //  4 - End of paragraph
+       { 'type': '/paragraph' }
+       //  5 - Beginning of table
+       { 'type': 'table' },
+       //  6 - Beginning of row
+       { 'type': 'row' },
+       //  7 - Beginning of cell
+       { 'type': 'cell' },
+       //  8 - Beginning of paragraph
+       { 'type': 'paragraph' },
+       //  9 - Plain content
+       'a',
+       // 10 - End of paragraph
+       { 'type': '/paragraph' },
+       // 11 - Beginning of list
+       { 'type': 'list' },
+       // 12 - Beginning of bullet list item
+       { 'type': 'item', 'styles': ['bullet'] },
+       // 13 - Plain content
+       'a',
+       // 14 - End of item
+       { 'type': '/item' },
+       // 15 - Beginning of nested bullet list item
+       { 'type': 'item', 'styles': ['bullet', 'bullet'] },
+       // 16 - Plain content
+       'b',
+       // 17 - End of item
+       { 'type': '/item' },
+       // 18 - Beginning of numbered list item
+       { 'type': 'item', 'styles': ['number'] },
+       // 19 - Plain content
+       'c',
+       // 20 - End of item
+       { 'type': '/item' },
+       // 21 - End of list
+       { 'type': '/list' },
+       // 22 - End of cell
+       { 'type': '/cell' }
+       // 23 - End of row
+       { 'type': '/row' }
+       // 24 - End of table
+       { 'type': '/table' }
+       // 25 - Beginning of paragraph
+       { 'type': 'paragraph' },
+       // 26 - Plain content
+       'a'
+       // 27 - End of paragraph
+       { 'type': '/paragraph' },
+];
+
+/*
+ * Example of content tree
+ * 
+ * Content trees are kept in sync with content data, providing a mapping 
between a structured user
+ * interface and a flat content model. They are made up of nodes which have 
some common properties:
+ *             type: Symbolic name of a block or sub-block component
+ *             length: Number of elements in content data between the element 
start and end
+ *             items: Information about the content between the element start 
and end
+ */
+var tree = [
+       {
+               'type': 'paragraph',
+               'length': 5,
+               //'content': ['a', ['b', { 'type': 'bold' }], ['c', { 'type': 
'italic' }]],
+       },
+       {
+               'type': 'table',
+               'length': 19,
+               'items': [
+                       {
+                               'type': 'row',
+                               'length': 17,
+                               'items': [
+                                       {
+                                               'type': 'cell',
+                                               'length': 15,
+                                               'items': {
+                                                       {
+                                                               'type': 
'paragraph',
+                                                               'length': 3
+                                                               //'content': 
['a']
+                                                       },
+                                                       {
+                                                               'type': 'list',
+                                                               'length': 12,
+                                                               'items': [
+                                                                       {
+                                                                               
'type': 'item',
+                                                                               
'styles': ['bullet'],
+                                                                               
'length': 3,
+                                                                               
//'content': ['a']
+                                                                       },
+                                                                       {
+                                                                               
'type': 'item',
+                                                                               
'styles': ['bullet', 'bullet'],
+                                                                               
'length': 3,
+                                                                               
//'content': ['b']
+                                                                       },
+                                                                       {
+                                                                               
'type': 'item',
+                                                                               
'styles': ['number'],
+                                                                               
'length': 3,
+                                                                               
//'content': ['c']
+                                                                       }
+                                                               ]
+                                                       }
+                                               }
+                                       }
+                               ]
+                       }
+               ]
+       },
+       {
+               'type': 'paragraph',
+               'length': 3,
+               //'content': ['a']
+       }
+];

Added: trunk/parsers/wikidom/lib/hype/es.js
===================================================================
--- trunk/parsers/wikidom/lib/hype/es.js                                (rev 0)
+++ trunk/parsers/wikidom/lib/hype/es.js        2011-09-24 00:14:53 UTC (rev 
97977)
@@ -0,0 +1,96 @@
+/**
+ * EditSurface namespace.
+ * 
+ * All classes and functions will be attached to this object to keep the 
global namespace clean.
+ */
+var es = {};
+
+/* Functions */
+
+/**
+ * Extends a constructor with the prototype of another.
+ * 
+ * When using this, it's required to include a call to the constructor of the 
parent class as the
+ * first code in the child class's constructor.
+ * 
+ * @example
+ *     // Define parent class
+ *     function Foo() {
+ *         // code here
+ *     }
+ *     // Define child class
+ *     function Bar() {
+ *         // Call parent constructor
+ *         Foo.call( this );
+ *     }
+ *     // Extend prototype
+ *     extend( Bar, Foo );
+ * 
+ * @static
+ * @method
+ * @param dst {Function} Class to extend
+ * @param src {Function} Base class to use methods from
+ */
+es.extend = function( dst, src ) {
+       var base = new src();
+       for ( var method in base ) {
+               if ( typeof base[method] === 'function' && !( method in 
dst.prototype ) ) {
+                       dst.prototype[method] = base[method];
+               }
+       }
+};
+
+/**
+ * Recursively compares string and number property between two objects.
+ * 
+ * A false result may be caused by property inequality or by properties in one 
object missing from
+ * the other. An asymmetrical test may also be performed, which checks only 
that properties in the
+ * first object are present in the second object, but not the inverse.
+ * 
+ * @static
+ * @method
+ * @param a {Object} First object to compare
+ * @param b {Object} Second object to compare
+ * @param asymmetrical {Boolean} Whether to check only that b contains values 
from a
+ * @returns {Boolean} If the objects contain the same values as each other
+ */
+es.compareObjects = function( a, b, asymmetrical ) {
+       var aValue, bValue, aType, bType;
+       var k;
+       for ( k in a ) {
+               aValue = a[k];
+               bValue = b[k];
+               aType = typeof aValue;
+               bType = typeof bValue;
+               if ( aType !== bType
+                               || ( ( aType === 'string' || aType === 'number' 
) && aValue !== bValue )
+                               || ( $.isPlainObject( aValue ) && 
!es.compareObjects( aValue, bValue ) ) ) {
+                       return false;
+               }
+       }
+       // If the check is not asymmetrical, recursing with the arguments 
swapped will verify our result
+       return asymmetrical ? true : es.compareObjects( b, a, true );
+};
+
+/**
+ * Gets a recursive copy of an object's string, number and plain-object 
property.
+ * 
+ * @static
+ * @method
+ * @param source {Object} Object to copy
+ * @returns {Object} Copy of source object
+ */
+es.copyObject = function( source ) {
+       var destination = {};
+       var key;
+       for ( key in source ) {
+               sourceValue = source[key];
+               sourceType = typeof sourceValue;
+               if ( sourceType === 'string' || sourceType === 'number' ) {
+                       destination[key] = sourceValue;
+               } else if ( $.isPlainObject( sourceValue ) ) {
+                       destination[key] = es.copyObject( sourceValue );
+               }
+       }
+       return destination;
+};


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to