password-manager/frontend/beta/js/YUI-extensions/data/XMLDataModel.js
2011-10-03 00:56:18 +01:00

275 lines
10 KiB
JavaScript

/**
* @class YAHOO.ext.grid.XMLDataModel
* This is an implementation of a DataModel used by the Grid. It works
* with XML data.
* <br>Example schema from Amazon search:
* <pre><code>
* var schema = {
* tagName: 'Item',
* id: 'ASIN',
* fields: ['Author', 'Title', 'Manufacturer', 'ProductGroup']
* };
* </code></pre>
* @extends YAHOO.ext.grid.LoadableDataModel
* @constructor
* @param {Object} schema The schema to use
* @param {XMLDocument} xml An XML document to load immediately
*/
YAHOO.ext.grid.XMLDataModel = function(schema, xml){
YAHOO.ext.grid.XMLDataModel.superclass.constructor.call(this, YAHOO.ext.grid.LoadableDataModel.XML);
/**@private*/
this.schema = schema;
this.xml = xml;
if(xml){
this.loadData(xml);
}
this.idSeed = 0;
};
YAHOO.extendX(YAHOO.ext.grid.XMLDataModel, YAHOO.ext.grid.LoadableDataModel, {
getDocument: function(){
return this.xml;
},
/**
* Overrides loadData in LoadableDataModel to process XML
* @param {XMLDocument} doc The document to load
* @param {<i>Function</i>} callback (optional) callback to call when loading is complete
* @param {<i>Boolean</i>} keepExisting (optional) true to keep existing data
* @param {<i>Number</i>} insertIndex (optional) if present, loaded data is inserted at the specified index instead of overwriting existing data
*/
loadData: function(doc, callback, keepExisting, insertIndex){
this.xml = doc;
var idField = this.schema.id;
var fields = this.schema.fields;
if(this.schema.totalTag){
this.totalCount = null;
var totalNode = doc.getElementsByTagName(this.schema.totalTag);
if(totalNode && totalNode.item(0) && totalNode.item(0).firstChild) {
var v = parseInt(totalNode.item(0).firstChild.nodeValue, 10);
if(!isNaN(v)){
this.totalCount = v;
}
}
}
var rowData = [];
var nodes = doc.getElementsByTagName(this.schema.tagName);
if(nodes && nodes.length > 0) {
for(var i = 0; i < nodes.length; i++) {
var node = nodes.item(i);
var colData = [];
colData.node = node;
colData.id = this.getNamedValue(node, idField, String(++this.idSeed));
for(var j = 0; j < fields.length; j++) {
var val = this.getNamedValue(node, fields[j], "");
if(this.preprocessors[j]){
val = this.preprocessors[j](val);
}
colData.push(val);
}
rowData.push(colData);
}
}
if(keepExisting !== true){
YAHOO.ext.grid.XMLDataModel.superclass.removeAll.call(this);
}
if(typeof insertIndex != 'number'){
insertIndex = this.getRowCount();
}
YAHOO.ext.grid.XMLDataModel.superclass.insertRows.call(this, insertIndex, rowData);
if(typeof callback == 'function'){
callback(this, true);
}
this.fireLoadEvent();
},
/**
* Adds a row to this DataModel and syncs the XML document
* @param {String} id The id of the row, if null the next row index is used
* @param {Array} cellValues The cell values for this row
* @return {Number} The index of the new row (if the model is sorted this index may not be accurate)
*/
addRow: function(id, cellValues){
var node = this.createNode(this.xml, id, cellValues);
cellValues.id = id || ++this.idSeed;
cellValues.node = node;
return YAHOO.ext.grid.XMLDataModel.superclass.addRow.call(this, cellValues);
},
/**
* Inserts a row into this DataModel and syncs the XML document
* @param {Number} index The index to insert the row
* @param {String} id The id of the row, if null the next row index is used
* @param {Array} cellValues The cell values for this row
* @return {Number} The index of the new row (if the model is sorted this index may not be accurate)
*/
insertRow: function(index, id, cellValues){
var node = this.createNode(this.xml, id, cellValues);
cellValues.id = id || ++this.idSeed;
cellValues.node = node;
return YAHOO.ext.grid.XMLDataModel.superclass.insertRow.call(this, index, cellValues);
},
/**
* Removes the row from DataModel and syncs the XML document
* @param {Number} index The index of the row to remove
*/
removeRow: function(index){
var node = this.data[index].node;
node.parentNode.removeChild(node);
YAHOO.ext.grid.XMLDataModel.superclass.removeRow.call(this, index, index);
},
getNode: function(rowIndex){
return this.data[rowIndex].node;
},
/**
* Override this method to define your own node creation routine for when new rows are added.
* By default this method clones the first node and sets the column values in the newly cloned node.
* In many instances this will not work and you will have to create the node manually.
* @param {XMLDocument} xmlDoc The xml document being used by this model
* @param {String/Number} id The row id
* @param {Array} colData The column data for the new node
* @return {XMLNode} The created node
*/
createNode: function(xmlDoc, id, colData){
var template = this.data[0].node;
var newNode = template.cloneNode(true);
var fields = this.schema.fields;
for(var i = 0, len = fields.length; i < len; i++){
var nodeValue = colData[i];
if(this.postprocessors[i]){
nodeValue = this.postprocessors[i](nodeValue);
}
this.setNamedValue(newNode, fields[i], nodeValue);
}
if(id){
this.setNamedValue(newNode, this.schema.idField, id);
}
template.parentNode.appendChild(newNode);
return newNode;
},
/**
* @private
* Convenience function looks for value in attributes, then in children tags - also
* normalizes namespace matches (ie matches ns:tag, FireFox matches tag and not ns:tag).
*/
getNamedValue: function(node, name, defaultValue){
if(!node || !name){
return defaultValue;
}
var nodeValue = defaultValue;
var attrNode = node.attributes.getNamedItem(name);
if(attrNode) {
nodeValue = attrNode.value;
} else {
var childNode = node.getElementsByTagName(name);
if(childNode && childNode.item(0) && childNode.item(0).firstChild) {
nodeValue = childNode.item(0).firstChild.nodeValue;
}else{
// try to strip namespace for FireFox
var index = name.indexOf(':');
if(index > 0){
return this.getNamedValue(node, name.substr(index+1), defaultValue);
}
}
}
return nodeValue;
},
/**
* @private
* Convenience function set a value in the underlying xml node.
*/
setNamedValue: function(node, name, value){
if(!node || !name){
return;
}
var attrNode = node.attributes.getNamedItem(name);
if(attrNode) {
attrNode.value = value;
return;
}
var childNode = node.getElementsByTagName(name);
if(childNode && childNode.item(0) && childNode.item(0).firstChild) {
childNode.item(0).firstChild.nodeValue = value;
}else{
// try to strip namespace for FireFox
var index = name.indexOf(':');
if(index > 0){
this.setNamedValue(node, name.substr(index+1), value);
}
}
},
/**
* Overrides DefaultDataModel.setValueAt to update the underlying XML Document
* @param {Object} value The new value
* @param {Number} rowIndex
* @param {Number} colIndex
*/
setValueAt: function(value, rowIndex, colIndex){
var node = this.data[rowIndex].node;
if(node){
var nodeValue = value;
if(this.postprocessors[colIndex]){
nodeValue = this.postprocessors[colIndex](value);
}
this.setNamedValue(node, this.schema.fields[colIndex], nodeValue);
}
YAHOO.ext.grid.XMLDataModel.superclass.setValueAt.call(this, value, rowIndex, colIndex);
},
/**
* Overrides getRowId in DefaultDataModel to return the ID value of the specified node.
* @param {Number} rowIndex
* @return {Number}
*/
getRowId: function(rowIndex){
return this.data[rowIndex].id;
},
addRows : function(rowData){
for(var j = 0, len = rowData.length; j < len; j++){
var cellValues = rowData[j];
var id = ++this.idSeed;
var node = this.createNode(this.xml, id, cellValues);
cellValues.node=node;
cellValues.id = cellValues.id || id;
YAHOO.ext.grid.XMLDataModel.superclass.addRow.call(this,cellValues);
}
},
insertRows : function(index, rowData){
// copy original array so it is not reversed
rowData = rowData.slice(0).reverse();
for(var j = 0, len = rowData.length; j < len; j++){
var cellValues = rowData[j];
var id = ++this.idSeed;
var node = this.createNode(this.xml, id, cellValues);
cellValues.id = cellValues.id || id;
cellValues.node = node;
YAHOO.ext.grid.XMLDataModel.superclass.insertRow.call(this, index, cellValues);
}
}
});
YAHOO.ext.grid.XMLQueryDataModel = function(){
YAHOO.ext.grid.XMLQueryDataModel.superclass.constructor.apply(this, arguments);
};
YAHOO.extendX(YAHOO.ext.grid.XMLQueryDataModel, YAHOO.ext.grid.XMLDataModel, {
getNamedValue: function(node, name, defaultValue){
if(!node || !name){
return defaultValue;
}
var nodeValue = defaultValue;
var childNode = cssQuery(name, node);
if(childNode && childNode[0]) {
nodeValue = childNode[0].firstChild.nodeValue;
}
return nodeValue;
}
});