123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- define("dojox/data/CdfStore", ["dojo", "dojox", "dojo/data/util/sorter"], function(dojo, dojox) {
- dojox.data.ASYNC_MODE = 0;
- dojox.data.SYNC_MODE = 1;
- dojo.declare("dojox.data.CdfStore", null, {
- // summary:
- // IMPORTANT: The CDF Store is designed to work with Tibco GI, and references Tibco's
- // JSX3 JavaScript library and will not work without it.
- //
- // The CDF Store implements dojo.data.Read, Write, and Identity api's. It is a local
- // (in memory) store that handles XML documents formatted according to the
- // Common Data Format (CDF) spec:
- // http://www.tibco.com/devnet/resources/gi/3_1/tips_and_techniques/CommonDataFormatCDF.pdf
- //
- // The purpose of this store is to provide a glue between a jsx3 CDF file and a Dijit.
- //
- // While a CDF document is an XML file, other than the initial input, all data returned
- // from and written to this store should be in object format.
- //
- // identity: [const] String
- // The unique identifier for each item. Defaults to "jsxid" which is standard for a CDF
- // document. Should not be changed.
- identity: "jsxid",
- //
- // url : String
- // The location from which to fetch the XML (CDF) document.
- url: "",
- //
- // xmlStr: String
- // A string that can be parsed into an XML document and should be formatted according
- // to the CDF spec.
- // example:
- // | '<data jsxid="jsxroot"><record jsxtext="A"/><record jsxtext="B" jsxid="2" jsxid="2"/></data>'
- xmlStr:"",
- //
- // data: Object
- // A object that will be converted into the xmlStr property, and then parsed into a CDF.
- data:null,
- //
- // label: String
- // The property within each item used to define the item.
- label: "",
- //
- // mode [const]: dojox.data.ASYNC_MODE | dojox.data.SYNC_MODE
- // This store supports syncronous fetches if this property is set to dojox.data.SYNC_MODE.
- mode:dojox.data.ASYNC_MODE,
-
- constructor: function(/* Object */ args){
- // summary:
- // Constructor for the CDF store. Instantiate a new CdfStore.
- //
- if(args){
- this.url = args.url;
- this.xmlStr = args.xmlStr || args.str;
- if(args.data){
- this.xmlStr = this._makeXmlString(args.data);
- }
- this.identity = args.identity || this.identity;
- this.label = args.label || this.label;
- this.mode = args.mode !== undefined ? args.mode : this.mode;
- }
- this._modifiedItems = {};
-
- this.byId = this.fetchItemByIdentity;
- },
-
- /* dojo.data.api.Read */
- getValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* value? */ defaultValue){
- // summary:
- // Return an property value of an item
- //
- return item.getAttribute(property) || defaultValue; // anything
- },
- getValues: function(/* jsx3.xml.Entity */ item, /* String */ property){
- // summary:
- // Return an array of values
- //
- // TODO!!! Can't find an example of an array in any CDF files
- //
- var v = this.getValue(item, property, []);
- return dojo.isArray(v) ? v : [v];
- },
- getAttributes: function(/* jsx3.xml.Entity */ item){
- // summary:
- // Return an array of property names
- //
- return item.getAttributeNames(); // Array
- },
- hasAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
- // summary:
- // Check whether an item has a property
- //
- return (this.getValue(item, property) !== undefined); // Boolean
- },
-
- hasProperty: function(/* jsx3.xml.Entity */ item, /* String */ property){
- // summary:
- // Alias for hasAttribute
- return this.hasAttribute(item, property);
- },
-
- containsValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* anything */ value){
- // summary:
- // Check whether an item contains a value
- //
- var values = this.getValues(item, property);
- for(var i = 0; i < values.length; i++){
- if(values[i] === null){ continue; }
- if((typeof value === "string")){
- if(values[i].toString && values[i].toString() === value){
- return true;
- }
- }else if(values[i] === value){
- return true; //boolean
- }
- }
- return false;//boolean
- },
- isItem: function(/* anything */ something){
- // summary:
- // Check whether the object is an item (jsx3.xml.Entity)
- //
- if(something.getClass && something.getClass().equals(jsx3.xml.Entity.jsxclass)){
- return true; //boolean
- }
- return false; //boolran
- },
- isItemLoaded: function(/* anything */ something){
- // summary:
- // Check whether the object is a jsx3.xml.Entity object and loaded
- //
- return this.isItem(something); // Boolean
- },
- loadItem: function(/* object */ keywordArgs){
- // summary:
- // Load an item
- // description:
- // The store always loads all items, so if it's an item, then it's loaded.
- },
- getFeatures: function(){
- // summary:
- // Return supported data APIs
- //
- return {
- "dojo.data.api.Read": true,
- "dojo.data.api.Write": true,
- "dojo.data.api.Identity":true
- }; // Object
- },
- getLabel: function(/* jsx3.xml.Entity */ item){
- // summary:
- // See dojo.data.api.Read.getLabel()
- //
- if((this.label !== "") && this.isItem(item)){
- var label = this.getValue(item,this.label);
- if(label){
- return label.toString();
- }
- }
- return undefined; //undefined
- },
- getLabelAttributes: function(/* jsx3.xml.Entity */ item){
- // summary:
- // returns an array of what properties of the item that were used
- // to generate its label
- // See dojo.data.api.Read.getLabelAttributes()
- //
- if(this.label !== ""){
- return [this.label]; //array
- }
- return null; //null
- },
-
- fetch: function(/* Object? */ request){
- // summary:
- // Returns an Array of items based on the request arguments.
- // description:
- // Returns an Array of items based on the request arguments.
- // If the store is in ASYNC mode, the items should be expected in an onComplete
- // method passed in the request object. If store is in SYNC mode, the items will
- // be return directly as well as within the onComplete method.
- // note:
- // The mode can be set on store initialization or during a fetch as one of the
- // parameters.
- //
- // query: String
- // The items in the store are treated as objects, but this is reading an XML
- // document. Further, the actual querying of the items takes place in Tibco GI's
- // jsx3.xml.Entity. Therefore, we are using their syntax which is xpath.
- // Note:
- // As conforming to a CDF document, most, if not all nodes are considered "records"
- // and their tagNames are as such. The root node is named "data".
- //
- // examples:
- // All items:
- // | store.fetch({query:"*"});
- // Item with a jsxid attribute equal to "1" (note you could use byId for this)
- // | store.fetch({query:"//record[@jsxid='1']"});
- // All items with any jsxid attribute:
- // | "//record[@jsxid='*']"
- // The items with a jsxid of '1' or '4':
- // | "//record[@jsxid='4' or @jsxid='1']"
- // All children within a "group" node (could be multiple group nodes):
- // "//group/record"
- // All children within a specific group node:
- // "//group[@name='mySecondGroup']/record"
- // Any record, anywhere in the document:
- // | "//record"
- // Only the records beneath the root (data) node:
- // | "//data/record"
- //
- // See:
- // http://www.tibco.com/devnet/resources/gi/3_7/api/html/jsx3/xml/Entity.html#method:selectNodes
- // http://www.w3.org/TR/xpath
- // http://msdn.microsoft.com/en-us/library/ms256086.aspx
- //
- // See dojo.data.Read.fetch():
- // onBegin
- // onComplete
- // onItem
- // onError
- // scope
- // start
- // count
- // sort
- //
- request = request || {};
- if(!request.store){
- request.store = this;
- }
- if(request.mode !== undefined){
- this.mode = request.mode;
- }
- var self = this;
-
- var errorHandler = function(errorData){
- if(request.onError){
- var scope = request.scope || dojo.global;
- request.onError.call(scope, errorData, request);
- }else{
- console.error("cdfStore Error:", errorData);
- }
- };
-
- var fetchHandler = function(items, requestObject){
- requestObject = requestObject || request;
- var oldAbortFunction = requestObject.abort || null;
- var aborted = false;
-
- var startIndex = requestObject.start?requestObject.start:0;
- var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
-
- requestObject.abort = function(){
- aborted = true;
- if(oldAbortFunction){
- oldAbortFunction.call(requestObject);
- }
- };
-
- var scope = requestObject.scope || dojo.global;
- if(!requestObject.store){
- requestObject.store = self;
- }
- if(requestObject.onBegin){
- requestObject.onBegin.call(scope, items.length, requestObject);
- }
- if(requestObject.sort){
- items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
- }
-
- if(requestObject.onItem){
- for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
- var item = items[i];
- if(!aborted){
- requestObject.onItem.call(scope, item, requestObject);
- }
- }
- }
- if(requestObject.onComplete && !aborted){
- if(!requestObject.onItem){
- items = items.slice(startIndex, endIndex);
- if(requestObject.byId){
- items = items[0];
- }
- }
- requestObject.onComplete.call(scope, items, requestObject);
- }else{
- items = items.slice(startIndex, endIndex);
- if(requestObject.byId){
- items = items[0];
- }
- }
- return items;
- };
-
- if(!this.url && !this.data && !this.xmlStr){
- errorHandler(new Error("No URL or data specified."));
- return false;
- }
- var localRequest = request || "*"; // use request for _getItems()
-
- if(this.mode == dojox.data.SYNC_MODE){
- // sync mode. items returned directly
- var res = this._loadCDF();
- if(res instanceof Error){
- if(request.onError){
- request.onError.call(request.scope || dojo.global, res, request);
- }else{
- console.error("CdfStore Error:", res);
- }
- return res;
- }
- this.cdfDoc = res;
-
- var items = this._getItems(this.cdfDoc, localRequest);
- if(items && items.length > 0){
- items = fetchHandler(items, request);
- }else{
- items = fetchHandler([], request);
- }
- return items;
-
- }else{
-
- // async mode. Return a Deferred.
- var dfd = this._loadCDF();
- dfd.addCallbacks(dojo.hitch(this, function(cdfDoc){
- var items = this._getItems(this.cdfDoc, localRequest);
- if(items && items.length > 0){
- fetchHandler(items, request);
- }else{
- fetchHandler([], request);
- }
- }),
- dojo.hitch(this, function(err){
- errorHandler(err, request);
- }));
-
- return dfd; // Object
- }
- },
-
- _loadCDF: function(){
- // summary:
- // Internal method.
- // If a cdfDoc exists, return it. Otherwise, get one from JSX3,
- // load the data or url, and return the doc or a deferred.
- var dfd = new dojo.Deferred();
- if(this.cdfDoc){
- if(this.mode == dojox.data.SYNC_MODE){
- return this.cdfDoc; // jsx3.xml.CDF
- }else{
- setTimeout(dojo.hitch(this, function(){
- dfd.callback(this.cdfDoc);
- }), 0);
- return dfd; // dojo.Deferred
- }
- }
-
- this.cdfDoc = jsx3.xml.CDF.Document.newDocument();
- this.cdfDoc.subscribe("response", this, function(evt){
- dfd.callback(this.cdfDoc);
- });
- this.cdfDoc.subscribe("error", this, function(err){
- dfd.errback(err);
- });
-
- this.cdfDoc.setAsync(!this.mode);
- if(this.url){
- this.cdfDoc.load(this.url);
- }else if(this.xmlStr){
- this.cdfDoc.loadXML(this.xmlStr);
- if(this.cdfDoc.getError().code){
- return new Error(this.cdfDoc.getError().description); // Error
- }
- }
-
- if(this.mode == dojox.data.SYNC_MODE){
- return this.cdfDoc; // jsx3.xml.CDF
- }else{
- return dfd; // dojo.Deferred
- }
- },
-
- _getItems: function(/* jsx3.xml.Entity */cdfDoc, /* Object */request){
- // summary:
- // Internal method.
- // Requests the items from jsx3.xml.Entity with an xpath query.
- //
- var itr = cdfDoc.selectNodes(request.query, false, 1);
- var items = [];
- while(itr.hasNext()){
- items.push(itr.next());
- }
- return items;
- },
- close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
- // summary:
- // See dojo.data.api.Read.close()
- },
- /* dojo.data.api.Write */
- newItem: function(/* object? */ keywordArgs, /* object? || String? */parentInfo){
- // summary:
- // Creates a jsx3.xml.Entity item and inserts it either inside the
- // parent or appends it to the root
- //
- keywordArgs = (keywordArgs || {});
- if(keywordArgs.tagName){
- // record tagName is automatic and this would add it
- // as a property
- if(keywordArgs.tagName!="record"){
- // TODO: How about some sort of group?
- console.warn("Only record inserts are supported at this time");
- }
- delete keywordArgs.tagName;
- }
- keywordArgs.jsxid = keywordArgs.jsxid || this.cdfDoc.getKey();
- if(this.isItem(parentInfo)){
- parentInfo = this.getIdentity(parentInfo);
- }
- var item = this.cdfDoc.insertRecord(keywordArgs, parentInfo);
- this._makeDirty(item);
-
- return item; // jsx3.xml.Entity
- },
-
- deleteItem: function(/* jsx3.xml.Entity */ item){
- // summary:
- // Delete an jsx3.xml.Entity (wrapper to a XML element).
- //
- this.cdfDoc.deleteRecord(this.getIdentity(item));
- this._makeDirty(item);
- return true; //boolean
- },
-
- setValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* almost anything */ value){
- // summary:
- // Set an property value
- //
- this._makeDirty(item);
- item.setAttribute(property, value);
- return true; // Boolean
- },
-
- setValues: function(/* jsx3.xml.Entity */ item, /* String */ property, /*array*/ values){
- // summary:
- // Set property values
- // TODO: Needs to be fully implemented.
- //
- this._makeDirty(item);
- console.warn("cdfStore.setValues only partially implemented.");
- return item.setAttribute(property, values);
-
- },
-
- unsetAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
- // summary:
- // Remove an property
- //
- this._makeDirty(item);
- item.removeAttribute(property);
- return true; // Boolean
- },
-
- revert: function(){
- // summary:
- // Invalidate changes (new and/or modified elements)
- // Resets data by simply deleting the reference to the cdfDoc.
- // Subsequent fetches will load the new data.
- // Note:
- // Any items outside the store will no longer be valid and may cause errors.
- //
- delete this.cdfDoc;
- this._modifiedItems = {};
- return true; //boolean
- },
-
- isDirty: function(/* jsx3.xml.Entity ? */ item){
- // summary:
- // Check whether an item is new, modified or deleted.
- // If no item is passed, checks if anything in the store has changed.
- //
- if(item){
- return !!this._modifiedItems[this.getIdentity(item)]; // Boolean
- }else{
- var _dirty = false;
- for(var nm in this._modifiedItems){ _dirty = true; break; }
- return _dirty; // Boolean
- }
- },
-
- /* internal API */
- _makeDirty: function(item){
- // summary:
- // Internal method.
- // Marks items as modified, deleted or new.
- var id = this.getIdentity(item);
- this._modifiedItems[id] = item;
- },
-
-
- _makeXmlString: function(obj){
- // summary:
- // Internal method.
- // Converts an object into an XML string.
- //
- var parseObj = function(obj, name){
- var xmlStr = "";
- var nm;
- if(dojo.isArray(obj)){
- for(var i=0;i<obj.length;i++){
- xmlStr += parseObj(obj[i], name);
- }
- }else if(dojo.isObject(obj)){
- xmlStr += '<'+name+' ';
- for(nm in obj){
- if(!dojo.isObject(obj[nm])){
- xmlStr += nm+'="'+obj[nm]+'" ';
- }
- }
- xmlStr +='>';
- for(nm in obj){
- if(dojo.isObject(obj[nm])){
- xmlStr += parseObj(obj[nm], nm);
- }
- }
- xmlStr += '</'+name+'>';
- }
- return xmlStr;
- };
- return parseObj(obj, "data");
- },
- /*************************************
- * Dojo.data Identity implementation *
- *************************************/
- getIdentity: function(/* jsx3.xml.Entity */ item){
- // summary:
- // Returns the identifier for an item.
- //
- return this.getValue(item, this.identity); // String
- },
- getIdentityAttributes: function(/* jsx3.xml.Entity */ item){
- // summary:
- // Returns the property used for the identity.
- //
- return [this.identity]; // Array
- },
- fetchItemByIdentity: function(/* Object || String */ args){
- // summary:
- // See dojo.data.api.Identity.fetchItemByIdentity(keywordArgs)
- //
- // Note:
- // This method can be synchronous if mode is set.
- // Also, there is a more finger friendly alias of this method, byId();
- if(dojo.isString(args)){
- var id = args;
- args = {query:"//record[@jsxid='"+id+"']", mode: dojox.data.SYNC_MODE};
- }else{
- if(args){
- args.query = "//record[@jsxid='"+args.identity+"']";
- }
- if(!args.mode){args.mode = this.mode;}
- }
- args.byId = true;
- return this.fetch(args); // dojo.Deferred || Array
- },
- byId: function(/* Object || String */ args){
- // stub. See fetchItemByIdentity
- }
-
- });
- return dojox.data.CdfStore;
- });
|