https://www.mediawiki.org/wiki/Special:Code/MediaWiki/113530
Revision: 113530 Author: tparscal Date: 2012-03-09 21:54:00 +0000 (Fri, 09 Mar 2012) Log Message: ----------- Removed duplicate static methods and members that were copied to ve.dm - lets just leave them in ve.dm.DocumentNode for now. Modified Paths: -------------- trunk/extensions/VisualEditor/modules/ve/dm/ve.dm.js Modified: trunk/extensions/VisualEditor/modules/ve/dm/ve.dm.js =================================================================== --- trunk/extensions/VisualEditor/modules/ve/dm/ve.dm.js 2012-03-09 21:50:24 UTC (rev 113529) +++ trunk/extensions/VisualEditor/modules/ve/dm/ve.dm.js 2012-03-09 21:54:00 UTC (rev 113530) @@ -3,549 +3,4 @@ * * All classes and functions will be attached to this object to keep the global namespace clean. */ -ve.dm = { - - /* Static Members */ - - /** - * Mapping of symbolic names and node model constructors. - */ - 'nodeModels': {}, - /** - * Mapping of symbolic names and nesting rules. - * - * Each rule is an object with the follwing properties: - * parents and children properties may contain one of two possible values: - * {Array} List symbolic names of allowed element types (if empty, none will be allowed) - * {Null} Any element type is allowed (as long as the other element also allows it) - * - * @example Paragraph rules - * { - * 'parents': null, - * 'children': [] - * } - * @example List rules - * { - * 'parents': null, - * 'children': ['listItem'] - * } - * @example ListItem rules - * { - * 'parents': ['list'], - * 'children': null - * } - * @example TableCell rules - * { - * 'parents': ['tableRow'], - * 'children': null - * } - */ - 'nodeRules': { - 'document': { - 'parents': null, - 'children': null - } - }, - - /* Static Methods */ - - /* - * Create child nodes from an array of data. - * - * These child nodes are used for the model tree, which is a space partitioning data structure - * in which each node contains the length of itself (1 for opening, 1 for closing) and the - * lengths of it's child nodes. - */ - 'createNodesFromData': function( data ) { - var currentNode = new ve.dm.BranchNode(); - for ( var i = 0, length = data.length; i < length; i++ ) { - if ( data[i].type !== undefined ) { - // It's an element, figure out it's type - var element = data[i], - type = element.type, - open = type.charAt( 0 ) !== '/'; - // Trim the "/" off the beginning of closing tag types - if ( !open ) { - type = type.substr( 1 ); - } - if ( open ) { - // Validate the element type - if ( !( type in ve.dm.DocumentNode.nodeModels ) ) { - throw 'Unsuported element error. No class registered for element type: ' + - type; - } - // Create a model node for the element - var newNode = new ve.dm.DocumentNode.nodeModels[element.type]( element, 0 ); - // Add the new model node as a child - currentNode.push( newNode ); - // Descend into the new model node - currentNode = newNode; - } else { - // Return to the parent node - currentNode = currentNode.getParent(); - if ( currentNode === null ) { - throw 'createNodesFromData() received unbalanced data: found closing ' + - 'without matching opening at index ' + i; - } - } - } else { - // It's content, let's start tracking the length - var start = i; - // Move forward to the next object, tracking the length as we go - while ( data[i].type === undefined && i < length ) { - i++; - } - // Now we know how long the current node is - currentNode.setContentLength( i - start ); - // The while loop left us 1 element to far - i--; - } - } - return currentNode.getChildren().slice( 0 ); - }, - /** - * Creates a document model from a plain object. - * - * @static - * @method - * @param {Object} obj Object to create new document model from - * @returns {ve.dm.DocumentNode} Document model created from obj - */ - 'newFromPlainObject': function( obj ) { - if ( obj.type === 'document' ) { - var data = [], - attributes = ve.isPlainObject( obj.attributes ) ? - ve.copyObject( obj.attributes ) : {}; - for ( var i = 0; i < obj.children.length; i++ ) { - data = data.concat( - ve.dm.DocumentNode.flattenPlainObjectElementNode( obj.children[i] ) - ); - } - return new ve.dm.DocumentNode( data, attributes ); - } - throw 'Invalid object error. Object is not a valid document object.'; - }, - /** - * Generates a hash of an annotation object based on it's name and data. - * - * @static - * @method - * @param {Object} annotation Annotation object to generate hash for - * @returns {String} Hash of annotation - */ - 'getHash': ( window.JSON && typeof JSON.stringify === 'function' ) ? - JSON.stringify : ve.dm.JsonSerializer.stringify, - /** - * Gets the index of the first instance of a given annotation. - * - * This method differs from ve.inArray because it compares hashes instead of references. - * - * @static - * @method - * @param {Array} annotations Annotations to search through - * @param {Object} annotation Annotation to search for - * @param {Boolean} typeOnly Whether to only consider the type - * @returns {Integer} Index of annotation in annotations, or -1 if annotation was not found - */ - 'getIndexOfAnnotation': function( annotations, annotation, typeOnly ) { - if ( annotation === undefined || annotation.type === undefined ) { - throw 'Invalid annotation error. Can not find non-annotation data in character.'; - } - if ( ve.isArray( annotations ) ) { - // Find the index of a comparable annotation (checking for same value, not reference) - for ( var i = 0; i < annotations.length; i++ ) { - // Skip over character data - used when this is called on a content data item - if ( typeof annotations[i] === 'string' ) { - continue; - } - if ( - ( - typeOnly && - annotations[i].type === annotation.type - ) || - ( - !typeOnly && - annotations[i].hash === ( - annotation.hash || ve.dm.DocumentNode.getHash( annotation ) - ) - ) - ) { - return i; - } - } - } - return -1; - }, - /** - * Gets a list of indexes of annotations that match a regular expression. - * - * @static - * @method - * @param {Array} annotations Annotations to search through - * @param {RegExp} pattern Regular expression pattern to match with - * @returns {Integer[]} List of indexes in annotations that match - */ - 'getMatchingAnnotations': function( annotations, pattern ) { - if ( !( pattern instanceof RegExp ) ) { - throw 'Invalid annotation error. Can not find non-annotation data in character.'; - } - var matches = []; - if ( ve.isArray( annotations ) ) { - // Find the index of a comparable annotation (checking for same value, not reference) - for ( var i = 0; i < annotations.length; i++ ) { - // Skip over character data - used when this is called on a content data item - if ( typeof annotations[i] === 'string' ) { - continue; - } - if ( pattern.test( annotations[i].type ) ) { - matches.push( annotations[i] ); - } - } - } - return matches; - }, - /** - * Sorts annotations of a character. - * - * This method modifies data in place. The string portion of the annotation character will always - * remain at the beginning. - * - * @static - * @method - * @param {Array} character Annotated character to be sorted - */ - 'sortCharacterAnnotations': function( character ) { - if ( !ve.isArray( character ) ) { - return; - } - character.sort( function( a, b ) { - var aHash = a.hash || ve.dm.DocumentNode.getHash( a ), - bHash = b.hash || ve.dm.DocumentNode.getHash( b ); - return typeof a === 'string' ? -1 : - ( typeof b === 'string' ? 1 : ( aHash == bHash ? 0 : ( aHash < bHash ? -1 : 1 ) ) ); - } ); - }, - /** - * Adds annotation hashes to content data. - * - * This method modifies data in place. - * - * @method - * @param {Array} data Data to add annotation hashes to - */ - 'addAnnotationHashesToData': function( data ) { - for ( var i = 0; i < data.length; i++ ) { - if ( ve.isArray( data[i] ) ) { - for ( var j = 1; j < data.length; j++ ) { - if ( data[i][j].hash === undefined ) { - data[i][j].hash = ve.dm.DocumentNode.getHash( data[i][j] ); - } - } - } - } - }, - /** - * Applies annotations to content data. - * - * This method modifies data in place. - * - * @method - * @param {Array} data Data to remove annotations from - * @param {Array} annotations Annotations to apply - */ - 'addAnnotationsToData': function( data, annotations ) { - if ( annotations && annotations.length ) { - for ( var i = 0; i < data.length; i++ ) { - if ( ve.isArray( data[i] ) ) { - data[i] = [data[i]]; - } - data[i] = [data[i]].concat( annotations ); - } - } - }, - /** - * Removes annotations from content data. - * - * This method modifies data in place. - * - * @method - * @param {Array} data Data to remove annotations from - * @param {Array} [annotations] Annotations to remove (all will be removed if undefined) - */ - 'removeAnnotationsFromData': function( data, annotations ) { - for ( var i = 0; i < data.length; i++ ) { - if ( ve.isArray( data[i] ) ) { - data[i] = data[i][0]; - } - } - }, - /** - * Creates an ve.ContentModel object from a plain content object. - * - * A plain content object contains plain text and a series of annotations to be applied to ranges of - * the text. - * - * @example - * { - * 'text': '1234', - * 'annotations': [ - * // Makes "23" bold - * { - * 'type': 'bold', - * 'range': { - * 'start': 1, - * 'end': 3 - * } - * } - * ] - * } - * - * @static - * @method - * @param {Object} obj Plain content object, containing a "text" property and optionally - * an "annotations" property, the latter of which being an array of annotation objects including - * range information - * @returns {Array} - */ - 'flattenPlainObjectContentNode': function( obj ) { - if ( !ve.isPlainObject( obj ) ) { - // Use empty content - return []; - } else { - // Convert string to array of characters - var data = obj.text.split(''); - // Render annotations - if ( ve.isArray( obj.annotations ) ) { - for ( var i = 0, length = obj.annotations.length; i < length; i++ ) { - var src = obj.annotations[i]; - // Build simplified annotation object - var dst = { 'type': src.type }; - if ( 'data' in src ) { - dst.data = ve.copyObject( src.data ); - } - // Add a hash to the annotation for faster comparison - dst.hash = ve.dm.DocumentNode.getHash( dst ); - // Apply annotation to range - if ( src.range.start < 0 ) { - // TODO: The start can not be lower than 0! Throw error? - // Clamp start value - src.range.start = 0; - } - if ( src.range.end > data.length ) { - // TODO: The end can not be higher than the length! Throw error? - // Clamp end value - src.range.end = data.length; - } - for ( var j = src.range.start; j < src.range.end; j++ ) { - // Auto-convert to array - if ( typeof data[j] === 'string' ) { - data[j] = [data[j]]; - } - // Append - data[j].push( dst ); - } - } - } - return data; - } - }, - /** - * Flatten a plain node object into a data array, recursively. - * - * TODO: where do we document this whole structure - aka "WikiDom"? - * - * @static - * @method - * @param {Object} obj Plain node object to flatten - * @returns {Array} Flattened version of obj - */ - 'flattenPlainObjectElementNode': function( obj ) { - var i, - data = [], - element = { 'type': obj.type }; - if ( ve.isPlainObject( obj.attributes ) ) { - element.attributes = ve.copyObject( obj.attributes ); - } - // Open element - data.push( element ); - if ( ve.isPlainObject( obj.content ) ) { - // Add content - data = data.concat( ve.dm.DocumentNode.flattenPlainObjectContentNode( obj.content ) ); - } else if ( ve.isArray( obj.children ) ) { - // Add children - only do this if there is no content property - for ( i = 0; i < obj.children.length; i++ ) { - // TODO: Figure out if all this concatenating is inefficient. I think it is - data = data.concat( ve.dm.DocumentNode.flattenPlainObjectElementNode( obj.children[i] ) ); - } - } - // Close element - TODO: Do we need attributes here or not? - data.push( { 'type': '/' + obj.type } ); - return data; - }, - /** - * Get a plain object representation of content data. - * - * @method - * @returns {Object} Plain object representation - */ - 'getExpandedContentData': function( data ) { - var stack = []; - // Text and annotations - function start( offset, annotation ) { - // Make a new verion of the annotation object and push it to the stack - var obj = { - 'type': annotation.type, - 'range': { 'start': offset } - }; - if ( annotation.data ) { - obj.data = ve.copyObject( annotation.data ); - } - stack.push( obj ); - } - function end( offset, annotation ) { - for ( var i = stack.length - 1; i >= 0; i-- ) { - if ( !stack[i].range.end ) { - if ( annotation ) { - // We would just compare hashes, but the stack doesn't contain any - if ( stack[i].type === annotation.type && - ve.compareObjects( stack[i].data, annotation.data ) ) { - stack[i].range.end = offset; - break; - } - } else { - stack[i].range.end = offset; - } - } - } - } - var left = '', - right, - leftPlain, - rightPlain, - obj = { 'text': '' }, - offset = 0, - i, - j; - for ( i = 0; i < data.length; i++ ) { - right = data[i]; - leftPlain = typeof left === 'string'; - rightPlain = typeof right === 'string'; - // Open or close annotations - if ( !leftPlain && rightPlain ) { - // [formatted][plain] pair, close any annotations for left - end( i - offset ); - } else if ( leftPlain && !rightPlain ) { - // [plain][formatted] pair, open any annotations for right - for ( j = 1; j < right.length; j++ ) { - start( i - offset, right[j] ); - } - } else if ( !leftPlain && !rightPlain ) { - // [formatted][formatted] pair, open/close any differences - for ( j = 1; j < left.length; j++ ) { - if ( ve.dm.DocumentNode.getIndexOfAnnotation( data[i] , left[j], true ) === -1 ) { - end( i - offset, left[j] ); - } - } - for ( j = 1; j < right.length; j++ ) { - if ( ve.dm.DocumentNode.getIndexOfAnnotation( data[i - 1], right[j], true ) === -1 ) { - start( i - offset, right[j] ); - } - } - } - obj.text += rightPlain ? right : right[0]; - left = right; - } - if ( data.length ) { - end( i - offset ); - } - if ( stack.length ) { - obj.annotations = stack; - } - // Copy attributes if there are any set - if ( !ve.isEmptyObject( this.attributes ) ) { - obj.attributes = ve.extendObject( true, {}, this.attributes ); - } - return obj; - }, - /** - * Checks if a data at a given offset is content. - * - * @example Content data: - * <paragraph> a b c </paragraph> <list> <listItem> d e f </listItem> </list> - * ^ ^ ^ ^ ^ ^ - * - * @static - * @method - * @param {Array} data Data to evaluate offset within - * @param {Integer} offset Offset in data to check - * @returns {Boolean} If data at offset is content - */ - 'isContentData': function( data, offset ) { - // Shortcut: if there's already content there, we will trust it's supposed to be there - return typeof data[offset] === 'string' || ve.isArray( data[offset] ); - }, - /** - * Checks if a data at a given offset is an element. - * - * @example Element data: - * <paragraph> a b c </paragraph> <list> <listItem> d e f </listItem> </list> - * ^ ^ ^ ^ ^ ^ - * - * @static - * @method - * @param {Array} data Data to evaluate offset within - * @param {Integer} offset Offset in data to check - * @returns {Boolean} If data at offset is an element - */ - 'isElementData': function( data, offset ) { - // TODO: Is there a safer way to check if it's a plain object without sacrificing speed? - return offset >= 0 && offset < data.length && data[offset].type !== undefined; - }, - /** - * Checks if an offset within given data is structural. - * - * Structural offsets are those at the beginning, end or surrounded by elements. This differs - * from a location at which an element is present in that elements can be safely inserted at a - * structural location, but not nessecarily where an element is present. - * - * @example Structural offsets: - * <paragraph> a b c </paragraph> <list> <listItem> d e f </listItem> </list> - * ^ ^ ^ ^ ^ - * - * @static - * @method - * @param {Array} data Data to evaluate offset within - * @param {Integer} offset Offset to check - * @returns {Boolean} Whether offset is structural or not - */ - 'isStructuralOffset': function( data, offset ) { - // Edges are always structural - if ( offset === 0 || offset === data.length ) { - return true; - } - // Structual offsets will have elements on each side - if ( data[offset - 1].type !== undefined && data[offset].type !== undefined ) { - if ( '/' + data[offset - 1].type === data[offset].type ) { - return false; - } - return true; - } - return false; - }, - /** - * Checks if elements are present within data. - * - * @static - * @method - * @param {Array} data Data to look for elements within - * @returns {Boolean} If elements exist in data - */ - 'containsElementData': function( data ) { - for ( var i = 0, length = data.length; i < length; i++ ) { - if ( data[i].type !== undefined ) { - return true; - } - } - return false; - } -}; +ve.dm = {}; _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs