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