/** Licensed Materials - Property of IBM, 5724-E76 and 5724-E77, (C) Copyright IBM Corp. 2009, 2010, 2011 - All Rights reserved. **/ if(!dojo._hasResource["com.ibm.data.ProxyHelper"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.ProxyHelper"] = true; dojo.provide( "com.ibm.data.ProxyHelper" ); dojo.require( "com.ibm.utilities" ); dojo.declare( "com.ibm.data.ProxyHelper", null, { // proxyURI: String // URI to the proxy servlet. proxyURI: "", urlThroughProxy: function(/*String*/ url) { return com.ibm.utilities.urlToProxyUrl(url ? url : "", this.proxyURI); } } ); } if(!dojo._hasResource["com.ibm.data.CatalogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.CatalogMixin"] = true; // Datastores written for use in the content search widget must inherit from this class to make the mapItem and prepareQuery function available to the widget // These functions are used to unify access to datastores, so the widget can query and access items the same way for every source dojo.provide("com.ibm.data.CatalogMixin"); dojo.declare("com.ibm.data.CatalogMixin", null, { searchable: true, mapItem: function(/*DatstoreItem*/item){ // summary: Returns a map containing parameters for the given datastore item // The keys must be: label, description, tags, rating, url, id, thumbnail // - label, description, url, id and thumbnail are strings // - tags is an array of strings // - rating is a string that contains a number, note that decimal values are allowed // Having all content search datastores create item maps with the same keys allows the widget to access information uniformly // If a datastore item does not have the same attributes for getValue as seen below, this function must be overridden // If a datastore item does not have an appropriate parameter to map to a key, that map entry can be omitted // An example for a datastore item with a title, summary, rating and identifier: // mapItem: function(/*DatastoreItem*/item){ // // does not include the "tags" and "url" keys // var map = {}; // map["label"] = this.getEPResolvedValue(item, "title", "untitled"); // map["description"] = this.getEPResolvedValue(item, "summary", null); // map["rating"] = this.getEPResolvedValue(item, "rating", "0.0"); // map["id"] = this.getEPResolvedValue(item, "identifier", null); // map["thumbnail"] = this.getEPResolvedValue(item, "thumbnail", null); // return map; // } var map = {}; map["label"] = this.getEPResolvedValue(item, "label", "untitled"); map["description"] = this.getEPResolvedValue(item, "description", null); map["tags"] = this.getEPResolvedValue(item, "tags", null); map["rating"] = this.getEPResolvedValue(item, "rating", "0.0"); map["url"] = this.getEPResolvedValue(item, "url", null); map["id"] = this.getEPResolvedValue(item, "id", null); map["thumbnail"] = this.getEPResolvedValue(item, "thumbnail", null); return map; }, prepareQuery: function(/*Object*/keywordArgs) { // summary: User overridable function to modify the keywordArgs object used to fetch results from the back end // Called after the keywordArgs.query property has been normalized to an object map but before any other processing has taken place to build the URL // After this function is called, anything in the keywordArgs.query map is appended to the fetch URL in the form (?|&)= &= &= ... // Can be used to extract other parts of the keywordArgs map to add properties to the query object that are dependent on back end implementation // Properties of the query object that are not part of the fetch URL should be deleted // The keywordArgs object is defined in dojo.data.api.Read.fetch and query will be an object containing the key "keywords" with an array of strings as a value // An example for a feed URL containing "searchTerms", "startIndex" and "count" parameters: // prepareQuery: function(/*Object*/keywordArgs) { // if(keywordArgs.query.keywords) { // // searchTerms is a comma separated list of search parameters // keywordArgs.query.searchTerms = keywordArgs.query.keywords.join(","); // delete keywordArgs.query.keywords; // } // if(keywordArgs.count) { // keywordArgs.query.count = keywordArgs.count; // if(keywordArgs.start == null) { // keywordArgs.start = 0; // } // keywordArgs.query.startIndex = keywordArgs.start; // } // } return keywordArgs; }, validateSelf: function(){ // summary: validates this datastore, if it does not validate the source will not be used // returns true for validation and false otherwise return true; }, getSourceLabel: function(){ return false; } }); } if(!dojo._hasResource["com.ibm.data.JsonStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.JsonStore"] = true; // A json store that can be used for the customize shelf // dojo.data.ItemFileReadstore already is a JSON store, this store extends it and overwrites the _fetchItems function to do an AND instead of an OR search for the given keywords // it also adds support for passing in parameters for an nls bundle and keywords dojo.provide("com.ibm.data.JsonStore"); dojo.require("dojo.data.ItemFileReadStore"); dojo.require("dojo.data.util.filter"); dojo.require("dojo.i18n"); dojo.declare("com.ibm.data.JsonStore", [dojo.data.ItemFileReadStore, com.ibm.data.ProxyHelper, com.ibm.data.CatalogMixin], { nlsBundle: null, constructor: function(/* Object */ keywordParameters){ this.contributions = keywordParameters.contributions || []; this.inherited("constructor",arguments); }, _getItemsFromLoadedData: function(/* Object */ dataObject){ this.inherited("_getItemsFromLoadedData",arguments); this.shelfBundle = dojo.i18n.getLocalization("com.ibm.bundles", "Shelf"); var localizationPackageName = dataObject.localizationPackageName; var localizationBundleName = dataObject.localizationBundleName; if(localizationPackageName && localizationBundleName){ this.nlsBundle = dojo.i18n.getLocalization(localizationPackageName, localizationBundleName); dojo.forEach(this._arrayOfAllItems, dojo.hitch(this, function(item) { if (item.label) { item.label[0] = (this.nlsBundle && this.nlsBundle[item.label[0]]) ? this.nlsBundle[item.label[0]] : item.label[0]; } else { item.label = []; } if (!item.label[0]) { item.label[0] = this.shelfBundle.add_content_untitled; } if (item.description) { item.description[0] = (this.nlsBundle && this.nlsBundle[item.description[0]]) ? this.nlsBundle[item.description[0]] : item.description[0]; } if (item.help) { item.help[0] = (this.nlsBundle && this.nlsBundle[item.help[0]]) ? this.nlsBundle[item.help[0]] : item.help[0]; } }) ); } }, mapItem: function(/*DatstoreItem*/item){ var map = {}; map.label = this.getValue(item, "label", null); map.description = this.getValue(item, "description", null); map.url = this.getValue(item, "url", null); map.id = this.getValue(item, "id", null); map.thumbnail = this.getValue(item, "thumbnail", null); map.help = this.getValue(item, "help", null); map.takeoverFcn = this.getValue(item,"takeover",null); return map; }, _fetchItems: function(/*Object*/ keywordArgs, /*Function*/ findCallback, /*Function*/ errorCallback){ var self = this; var filter = function(requestArgs, arrayOfItems){ var items = []; var i = 0; if(requestArgs.query){ var filterKeywords = requestArgs.query.keywords; var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false; var regexpList = {}; var currFilter = null; for(i = 0; i < filterKeywords.length; i++){ currFilter = filterKeywords[i]; if(typeof currFilter === "string"){ regexpList[currFilter] = dojo.data.util.filter.patternToRegExp("*"+currFilter+"*", ignoreCase); } } for(i = 0; i < arrayOfItems.length; ++i){ var match = true; var candidateItem = arrayOfItems[i]; if(candidateItem === null){ match = false; } else { for(var j = 0; j < filterKeywords.length; j++){ currFilter = filterKeywords[j]; if(!self._containsValue(candidateItem, 'label', currFilter, regexpList[currFilter]) && !self._containsValue(candidateItem, 'description', currFilter, regexpList[currFilter])){ match = false; } } } if(match){ items.push(candidateItem); } } findCallback(items, requestArgs); } else { for(i = 0; i < arrayOfItems.length; ++i){ var item = arrayOfItems[i]; if(item !== null){ items.push(item); } } findCallback(items, requestArgs); } }; if(this._loadFinished){ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions)); } else { if(this._jsonFileUrl){ if(this._loadInProgress){ this._queuedFetches.push({args: keywordArgs, filter: filter}); } else { this._loadInProgress = true; var getArgs = { url: self._jsonFileUrl, handleAs: "json-comment-optional" }; var getHandler = dojo.xhrGet(getArgs); getHandler.addCallback(function(data){ var endFcn = function(data) { try{ self._getItemsFromLoadedData(data); self._loadFinished = true; self._loadInProgress = false; filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions)); self._handleQueuedFetches(); } catch(e) { self._loadFinished = true; self._loadInProgress = false; errorCallback(e, keywordArgs); } }; var contributionCount = 0; var contributionLen = self.contributions.length; if(contributionLen == 0) endFcn(data); for(var i = 0; i < contributionLen; i++) { contribGetArgs = { url: self.contributions[i].indexOf("/") == 0 ? self.contributions[i] : ibmCfg.themeConfig.themeRootURI + "/" + self.contributions[i], handleAs: "json-comment-optional" } var contribGetHandler = dojo.xhrGet(contribGetArgs); contribGetHandler.addCallback(function(contributionData){ // get the bundle if one is provided var contribNlsJsonBundle = null; if(contributionData.localizationPackageName && contributionData.localizationBundleName){ contribNlsJsonBundle = dojo.i18n.getLocalization(contributionData.localizationPackageName, contributionData.localizationBundleName); } if(contribNlsJsonBundle != null) { for(var cdi = 0, len = contributionData.items.length; cdi < len; cdi++) { var label = contributionData.items[cdi].label; contributionData.items[cdi].label = contribNlsJsonBundle[label] ? contribNlsJsonBundle[label] : label; } } data.items = contributionData.items.concat(data.items); contributionCount++; if(contributionCount == contributionLen) endFcn(data); }); contribGetHandler.addErrback(function(error){ endFcn(data); }); } }); getHandler.addErrback(function(error){ self._loadInProgress = false; errorCallback(error, keywordArgs); }); } } else if(this._jsonData){ try{ this._loadFinished = true; this._getItemsFromLoadedData(this._jsonData); this._jsonData = null; filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions)); }catch(e){ errorCallback(e, keywordArgs); } } else { errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs); } } } }); } if(!dojo._hasResource["com.ibm.portal.xpath"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.portal.xpath"] = true; dojo.provide( "com.ibm.portal.xpath" ); // generic impl com.ibm.portal.xpath.evaluateXPath = function(/*String*/xpathExpr, /*DOMDocument*/ doc, /*Object{prefix:ns,prefix2:ns2,...}*/namespaces) { if (typeof ActiveXObject != "undefined") { return com.ibm.portal.xpath.ie.evaluateXPath(xpathExpr, doc, namespaces); } else { return com.ibm.portal.xpath.gecko.evaluateXPath(xpathExpr, doc, namespaces); } } // IE Implementation dojo.provide( "com.ibm.portal.xpath.ie" ); com.ibm.portal.xpath.ie.evaluateXPath = function(/*String*/xpathExpr, /*DOMDocument*/ doc, /*Object{prefix:ns,prefix2:ns2,...}*/namespaces) { if (namespaces) { var ns = ""; for (var prop in namespaces) { ns += "xmlns:"+prop+"='"+namespaces[prop]+"' "; } if (doc.ownerDocument) { doc.ownerDocument.setProperty("SelectionNamespaces", ns); } else { doc.setProperty("SelectionNamespaces", ns); } } var result = doc.selectNodes(xpathExpr); var thisResult; var resultSet = []; var len = 0; for (var i=0; i 0; if(prop) { var nodes = results; results = []; for(var i = 0; i < nodes.length; i++) { results[i] = this._getNodeProp(nodes[i], prop); } } return {found: found, value: results}; }, _getNodeByXpath: function(/*Object*/ context, /*String*/expr, /*String?*/ prop) { // summary: Gets a node using the XPath expr relative to // the context (not relative to the document root itself). Returns an object allowing // the caller to determine if the actual value requested is null or if there were no nodes // found at all. // context: The XML element to use as the root context for the XPath query // expr: XPath expression used to find the node // prop: Optional prop to specify to get an property of the node instead of the node itself. // returns: Object with two properties: // - { // - found: Boolean indicating if any nodes were found matching the XPath expression // - value: DOMNode|Object the first node found by the expression, or the property of the first // - node found by the expression as specified by the prop argument // - } var result = this._getNodesByXpath(context, expr); var found = result.found; if(!found) return {found: found}; var nodes = result.value; if(nodes.length > 0) { if(prop) return {found: true, value: this._getNodeProp(nodes[0], prop)}; else return {found: true, value: nodes[0]}; } return {found: false}; }, _getNodeProp: function(/*DOMNode*/ node, /*String*/ prop) { // summary: Function to provide cross-browser support for properties of a queried // node. If the prop string is not special, control will be passed to the // handleConstructs function for this object. // Currently only supports textContent as a special property since not // all browsers implement the textContent property of DOM nodes. switch(prop) { case "textContent": return dojox.data.dom.textContent(node); default: return this.handleConstructs(node, prop); } }, handleConstructs: function(/*DOMNode*/ node, /*String*/ prop) { // summary: Allows a data store to return complex javascript objects // or alternative values when a specific non-standard property of a node // is requested. Example: An Atom Feed data store may create a special // Person Construct when querying an element. This construct // may provide accessor functions to the name, email, and URI of the author. // See http://tools.ietf.org/html/rfc4287#section-3.2 // Constructs that are created here should implement and handle write functionality // themselves as this module will not handle writes to constructs natively. return node[prop]; }, _setNodeProp: function(/*DOMNode*/ node, /*String*/ prop, /*Object*/ value) { // summary: Allows a data store to set properties of a node in a cross-browser // way for properties that may be non-standard. // Currently only supports textContent as a special property since not // all browsers implement the textContent property of DOM nodes. // Note that for special constructs that are created using the handleConstructs // function, write support is handled by the returned construct object not by // this module's functions. switch(prop) { case "textContent": dojox.data.dom.textContent(node, value); break; default: node[prop] = value; } }, _getAttrsByXpath: function(/*Object*/ context, /*String*/ expr, /*String*/attrName) { // summary: Gets an array of attribute values for the nodes returned // using the XPath expr relative to the context (not relative to the // document root itself). The attributes are retrieved using the // getAttributeNS/getAttribute functions of the nodes. Returns an object allowing // the caller to determine if the actual value requested is null or if there were no nodes // found at all. // context: The XML element to use as the root context for the XPath query // expr: XPath expression used to find the nodes // attrName: The name of the attribute to get from each node returned by the XPath expr. // If the attribute is namespaced, it will be split into its parts using the : delimiter // and the namespace used during lookup will be the one keyed in this item or store's // namespace map. // returns: Object with two properties: // - { // - found: Boolean indicating if any nodes were found matching the XPath expression // - value: Array the attribute values of the nodes found by the expression // - } var result = this._getNodesByXpath(context, expr); var found = result.found; if(!found) return {found: found}; var nodes = result.value; var attrs = []; for(var i = 0; i < nodes.length; i++) { attrs[i] = this._getAttribute(nodes[i], attrName); } return {found: found, value: attrs}; }, _getAttrByXpath: function(/*Object*/ context, /*String*/expr, /*String*/ attrName) { // summary: Gets the attribute value of the first node returned // using the XPath expr relative to the context (not relative to the // document root itself). The attribute is retrieved using the // getAttributeNS/getAttribute functions of the node. Returns an object allowing // the caller to determine if the actual value requested is null or if there were no nodes // found at all. // context: The XML element to use as the root context for the XPath query // expr: XPath expression used to find the node // attrName: The name of the attribute to get from the node returned by the XPath expr. // If the attribute is namespaced, it will be split into its parts using the : delimiter // and the namespace used during lookup will be the one keyed in this item or store's // namespace map. // returns: Object with two properties: // - { // - found: Boolean indicating if any nodes were found matching the XPath expression // - value: Array the attribute value of the first node found by the expression // - } var result = this._getNodeByXpath(context, expr); var found = result.found; if(!found) return {found: found}; var node = result.value; return {found: found, value: this._getAttribute(node, attrName)}; }, _extractNSInfo: function(str) { // summary: Extracts the namespace URI, prefix, and local name of a QName according to this // object's namespaces map that includes known prefixes and their namespace URIs. If // the string is not namespaced, then the namespaceURI and prefix are set to null, and // the localName is set to the str as-is. // str: QName to extract namespace from // returns: Map - {namespaceURI: namespaceURI, prefix: prefix, localName: localName} return com.ibm.domUtilities.nsInfo(str, this.namespaces); }, _getAttribute: function(/*DOMNode*/ node, /*String*/ attrName) { // summary: Gets an attribute of the given node, supporting namespaced and // non-namespaced attributes. // node: Node to retrieve the attribute value from // attrName: The name of the attribute to get from the node. // If the attribute is namespaced, it will be split into its parts using the : delimiter // and the namespace used during lookup will be the one keyed in this item or store's // namespace map. return com.ibm.domUtilities.getAttribute(node, attrName, this.namespaces); }, _setAttribute: function(/*DOMNode*/ node, /*String*/ attrName, /*String*/ value) { // summary: Sets an attribute of the given node, supporting namespaced and // non-namespaced attributes. // node: Node to set the attribute value on // attrName: The name of the attribute to set on the node. // If the attribute is namespaced, it will be split into its parts using the : delimiter // and the namespace used during lookup will be the one keyed in this item or store's // namespace map. // value: The actual value to store in the node at the key attribute specified by attrName. if(value) com.ibm.domUtilities.setAttribute(node, attrName, value, this.namespaces); else com.ibm.domUtilities.removeAttribute(node, attrName, this.namespaces); }, _setNodesByXpath: function(/*Object*/ context, /*String*/expr, /*String*/ prop, /*Array*/values) { // Empty for now since there's little use for this type of function, particularly due to // mismatches between the number of values and number of returned nodes. //var nodes = this._getNodesByXpath(context, expr); return false; }, _setNodeByXpath: function(/*Object*/ context, /*String*/expr, /*String*/ prop, /*Object*/value) { // summary: Will replace a node returned by the xpath expr query with the value object // if prop is null. If prop exists, the specified property on the returned node // will be set to the value. // If no node is found that matches the xpath expr, this function returns false to indicate // failure to the caller for further processing if necessary. // context: The DOM element to use as the root context for the XPath query // expr: XPath expression used to find the node // prop: Optional prop to specify to set a property of the node instead of the node itself. // returns: true if the set operation was successful, false otherwise var result = this._getNodeByXpath(context, expr); var found = result.found; if(!found) return false; var node = result.value; if(prop) { this._setNodeProp(node, prop, value); } else { dojo.place(value, node, "before"); node.parentNode.removeChild(node); delete node; } return true; }, _setAttrsByXpath: function(/*Object*/ context, /*String*/expr, /*String*/ attrName, /*Array*/values) { // Empty for now since there's little use for this type of function, particularly due to // mismatches between the number of values and number of returned nodes. //var nodes = this._getNodesByXpath(context, expr); return false; }, _setAttrByXpath: function(/*Object*/ context, /*String*/expr, /*String*/ attrName, /*String*/value) { // summary: The specified DOM attribute on the returned node will be set to the value. // If no node is found that matches the xpath expr, this function returns false to indicate // failure to the caller for further processing if necessary. // context: The DOM element to use as the root context for the XPath query // expr: XPath expression used to find the node // attrName: The name of the attribute to get from the node returned by the XPath expr. // If the attribute is namespaced, it will be split into its parts using the : delimiter // and the namespace used during lookup will be the one keyed in this item or store's // namespace map. // returns: true if the set operation was successful, false otherwise var result = this._getNodeByXpath(context, expr); var found = result.found; if(!found) return false; var node = result.value; this._setAttribute(node, attrName, value); return true; }, _createNSElement: function(doc, tagName, attrMap) { // summary: Cross-browser implementation for creating a namespaced node using the namespace // map in this object to resolve prefixes to namespace URIs. // If the tagName is not namespaced or a namespace URI cannot be resolved // using this object's namespaces map, a non-namespaced element will be created instead. // doc: DOMDoc document object to use to create the DOM node. // tagName: String name for the tag name of the element to create. // Can be namespaced like prefix:localName or not like localName. // attrMap: Optional argument specifying a key-value map of attributes to be // set on the node before returning it. // returns: DOMNode A new DOM node that was created by doc return com.ibm.domUtilities.createElement(doc, tagName, this.namespaces, attrMap); }, __getClassName: function() { var cls = this.declaredClass; if(!cls) return "Object"; var idx = cls.lastIndexOf("."); if(idx > -1 && (idx < cls.length - 1)) cls = cls.substring(idx + 1); return cls; } } ); dojo.declare( "com.ibm.data._XmlConstruct", com.ibm.data.XpathHelper, { // summary: Provides a base construct for modules to extend when building constructs // to be returned by queries using this module's APIs. isConstruct: true, toString: function() { // summary: Default toString method for this object so that it can be inserted // in a string and be properly represented. Overridable by extensions of this // module. return "<" + this.__getClassName() + ">"; } } ); } if(!dojo._hasResource["com.ibm.data.AtomFeedStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.AtomFeedStore"] = true; dojo.provide( "com.ibm.data.AtomFeedStore" ); dojo.require( "com.ibm.data.XpathHelper" ); dojo.require( "dojo.date.stamp" ); dojo.declare( "com.ibm.data.AtomFeedStore", com.ibm.data.XpathHelper, { // namespaces: Map // Map of namespaces used by the ATOM feeds namespaces: { "atom" : "http://www.w3.org/2005/Atom" }, // _features: Map // Map of features that this store supports. // This generic AtomFeedStore only supports Read and Identity. _features: { 'dojo.data.api.Read': true, 'dojo.data.api.Write': false, 'dojo.data.api.Identity': false, 'dojo.data.api.Notification': false }, attrMappings: { title: {xpath: "./atom:title", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:title"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, id: {xpath: "./atom:id", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:id"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, updated: {xpath: "./atom:updated", targetName: "AtomDateConstruct", type: "node"}, category: {xpath: "./atom:category", targetName: "term", type: "attr", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:category"); this._setAttribute(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, categoryScheme: {xpath: "./atom:category", targetName: "scheme", type: "attr", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:category"); this._setAttribute(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, categoryLabel: {xpath: "./atom:category", targetName: "label", type: "attr", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:category"); this._setAttribute(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, contributer: {xpath: "./atom:contributer", targetName: "AtomPersonConstruct", type: "node"}, author: {xpath: "./atom:author", targetName: "AtomPersonConstruct", type: "node"}, summary: {xpath: "./atom:summary", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:summary"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, source: {xpath: "./atom:source", type: "node", setter: function(item, rule, value){ item.element.appendChild(value); return true; } }, rights: {xpath: "./atom:rights", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:rights"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, selfLink: {xpath: "./atom:link[@rel='self']", targetName: "href", type: "attr", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:link"); this._setAttribute(elem, "rel", "self"); this._setAttribute(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, link: {xpath: "./atom:link[@href]", targetName: "href", type: "attr", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:link"); this._setAttribute(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, content: {xpath: "./atom:content", type: "node", setter: function(item, rule, value){ item.element.appendChild(value); return true; } } }, // timeout: Number milliseconds to wait for a response for any request // sent by this data store. timeout: null, // itemClass: String the class used to represent items of this store itemClass: "com.ibm.data.AtomEntryItem", constructor: function( /*Map*/ args ) { // summary: Creates a new instance of a com.ibm.data.AtomFeedStore // args: Map // The args map will be used to replace any default values of this store with the // values specified in the args map. dojo.mixin(this, args); this._dirtyItems = []; this._deletedItems = []; this._newItems = []; }, toString: function() { return "<" + this.__getClassName() + ": " + this.url + ">"; }, handleConstructs: function(/*DOMNode*/ node, /*String*/ prop) { switch(prop) { case "AtomPersonConstruct": return new com.ibm.data.AtomPersonConstruct(node); case "AtomDateConstruct": return new com.ibm.data.AtomDateConstruct(node); default: return this.inherited(arguments); } }, _assertIsItem: function(/*Object*/ something) { // summary: Throws an exception if the argument is not a valid item in this store. // something: Object to check to determine if it is an item in this store. if (!this.isItem(something)) { throw new Error(something + " is not an item in this store"); } return true; }, _assertIsString: function(/*Object*/ something) { // summary: Throws an exception if the argument is not a string. // something: Object to check to determine if it is a string. if (!dojo.isString(something)) { throw new Error(something + " is not a string"); } return true; }, _getItem: function(args) { // summary: Overridable function to create a new item in this store based on the args map. var cls = this.itemClass; if(dojo.isString(cls)) { cls = dojo.getObject(this.itemClass); } return new cls({store: this, element: args.element, loaded: true}); }, /********************************/ /** **/ /** **/ /** Public **/ /** **/ /** **/ /********************************/ getFeedValue: function( /*String*/ attribute, /*Value?*/ defaultValue) { var ret = defaultValue; this._assertIsString(attribute); var mapRule = this.getMapRule(attribute, this.attrMappings); // if mapRule is null here, then we couldn't find one in the map, // so we can't proceed successfully if(mapRule) { ret = this.getValueByMapping(null, mapRule, defaultValue); } return ret; }, getFeedValues: function( /*String*/ attribute) { var ret = []; this._assertIsString(attribute); var mapRule = this.getMapRule(attribute, this.attrMappings); // if mapRule is null here, then we couldn't find one in the map, // so we can't proceed successfully if(mapRule) { ret = this.getValuesByMapping(null, mapRule); } return ret; }, extractEmptyFeed: function() { var toClone = this._getNodeByXpath(this._feed, "//atom:feed").value; var feedDOM = toClone.cloneNode(false); // get all non-entry nodes var result = this._getNodesByXpath(toClone, "/atom:feed/*"); var nodes = result.value; for(var i = 0; i < nodes.length; i++) { if(nodes[i].nodeName != "atom:entry") { feedDOM.appendChild(nodes[i].cloneNode(true)); } } return feedDOM; }, extractSingleEntryFeed: function(/* com.ibm.data.AtomEntryItem */ item ) { var ret = this.extractEmptyFeed(); ret.appendChild(item.element.cloneNode(true)); return ret; }, extractMultipleEntryFeed: function(/* Array */ items ) { var ret = this.extractEmptyFeed(); dojo.forEach(items, function(item){ ret.appendChild(item.element.cloneNode(true)); }); return ret; }, getEPResolvedValue: function( /* com.ibm.data.AtomEntryItem */ item, /* String */ attribute, /* Value? */ defaultValue){ var ret = null; if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings); // if mapRule is null here, then we couldn't find one in the map, // so we return the defaultValue if (!mapRule) { return ret; } ret = this.getValueByMapping(item, mapRule, defaultValue); } if (!ret) { ret = defaultValue; } ret = com.ibm.mashups.enabler.utils.EndpointHelper.resolve(ret); return ret; }, /********************************/ /** **/ /** **/ /** dojo.data.api.Identity **/ /** **/ /** **/ /********************************/ getIdentity: function(/* item */ item) { // summary: See dojo.data.api.Identity.getIdentity() // returns: The entry's unique identifier if (item._id) { return item._id; } // this is a common operation... so cache this on the item itself var attrs = this.getIdentityAttributes(item); var id = ""; dojo.forEach(attrs, function(attr){ id += this.getEPResolvedValue(item, attr, ""); }, this); item._id = id; return id; }, getIdentityAttributes: function(/* item */ item){ // summary: // Returns an array of attribute names that are used to generate the identity. // For most stores, this is a single attribute, but for some complex stores // such as RDB backed stores that use compound (multi-attribute) identifiers // it can be more than one. If the identity is not composed of attributes // on the item, it will return null. This function is intended to identify // the attributes that comprise the identity so that so that during a render // of all attributes, the UI can hide the the identity information if it // chooses. // item: // The item from the store from which to obtain the array of public attributes that // compose the identifier, if any. return ['id']; // Array }, fetchItemByIdentity: function(/* object */ keywordArgs){ // summary: // Given the identity of an item, this method returns the item that has // that identity through the onItem callback. Conforming implementations // should return null if there is no item with the given identity. // Implementations of fetchItemByIdentity() may sometimes return an item // from a local cache and may sometimes fetch an item from a remote server, // // keywordArgs: // An anonymous object that defines the item to locate and callbacks to invoke when the // item has been located and load has completed. The format of the object is as follows: // { // identity: string|object, // onItem: Function, // onError: Function, // scope: object // } // The *identity* parameter. // The identity parameter is the identity of the item you wish to locate and load // This attribute is required. It should be a string or an object that toString() // can be called on. // // The *onItem* parameter. // Function(item) // The onItem parameter is the callback to invoke when the item has been loaded. It takes only one // parameter, the item located, or null if none found. // // The *onError* parameter. // Function(error) // The onError parameter is the callback to invoke when the item load encountered an error. It takes only one // parameter, the error object // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onError, etc) will be invoked in the context of the scope object. // In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global. // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global, item, request) // not implemented by the AtomFeedStore since this supports generic Atom Feeds and therefore // can't generate a URL to get a particular item by its identity without additional knowledge // of how the feed URLs are created... however, this can be overridden by a application consuming // an instance of this store that does have knowledge of the particular feed URLs return false; }, /********************************/ /** **/ /** **/ /** dojo.data.api.Read **/ /** **/ /** **/ /********************************/ getValue: function( /* com.ibm.data.AtomEntryItem */ item, /* String */ attribute, /* Value? */ defaultValue){ var ret = null; if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings); // if mapRule is null here, then we couldn't find one in the map, // so we return the defaultValue if (!mapRule) { return ret; } ret = this.getValueByMapping(item, mapRule, defaultValue); } if (!ret) { ret = defaultValue; } return ret; }, getValues: function(/* com.ibm.data.AtomEntryItem */ item, /* String */ attribute){ if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings); // if mapRule is null here, then we couldn't find one in the map, // so we return an empty list if (!mapRule) { return []; } return this.getValuesByMapping(item, mapRule); } return []; }, _extractParameterizedMapKey: function( /*String*/ mapKey ) { // summary: Expands a mapKey and determines if it is a key to a parameterizable map rule. // If it is, extract the parameters from the argument and return the base key name // used to look up the correct map rule in this object's attrMappings without the parameter // values, and also return the parameters as an array of strings. // If the mapKey is not the key of a parameterizable map rule, just return the key and a // null parameter array. // returns: Object {key: stringMapKey, parameters: arrayOfParameterValues} if(mapKey.charAt(mapKey.length - 1) == '$') { var start = mapKey.indexOf('['); var end = mapKey.lastIndexOf(']'); if(start > -1 && end > start) { var baseName = mapKey.substring(0, start) + "$"; var parms = mapKey.substring(start + 1, end).split(','); return {key: baseName, parameters: parms}; } } return {key: mapKey, parameters: null}; }, getMapRule: function( /*String*/ mapKey, /*Object*/map, /*Boolean?*/ multiValued ) { // summary: Takes a mapKey string that ends in '$' to key into this object's // attrMappings, and then parameterizes the map it finds according // to the parameters found within the '[...]' block of the mapKey. // Example: A mapKey of 'localizedName[edward, tj]$' would lookup // the key 'localizedName$' in this.attrMappings. // The resulting map rule would be used as a template for the returned // map rule. The parameters used to parameterize the template would // be ['edward', 'tj'] and would match the ${0} and ${1} parameters // in the map rule respectively. // returns: The map rule specified by this mapKey after all parameters have been // bound to their values as specified by the caller. var obj = this._extractParameterizedMapKey(mapKey); var rule = map[obj.key]; if (!rule) { return rule; } // if this is not a parameterized map rule, just return the map rule as-is if(!obj.parameters) { rule._ruleKey = obj.key; return rule; } // otherwise, parameterize the map by using the array of parameters passed into the attribute // like this -> attribute[parm1, parm2, parm3]$ // parameterized attributes always end with $ var expRule = {}; for(var prop in rule) { if(dojo.isString(rule[prop])) { expRule[prop] = dojo.string.substitute(rule[prop], obj.parameters, function(part){ return dojo.string.trim(part); }); } else { expRule[prop] = rule[prop]; } } expRule._parameters = obj.parameters; expRule._ruleKey = obj.key; expRule.multiValued = !!multiValued; return expRule; }, getValuesByMapping: function( /* com.ibm.data.AtomEntryItem */ item, /* Object */ mapping){ // summary: Executes the correct query API using the mapping argument to // retrieve the desired attribute value for the item as specified by the // mapping. // mapping: A mapping object from this object's attrMappings var ret = null; var xpath = mapping.xpath; var type = mapping.type; var targetName = mapping.targetName; var context = this._feed; if(item) { this._assertIsItem(item); context = item.element; } if(type == "attr") { ret = this._getAttrsByXpath(context, xpath, targetName); } else { ret = this._getNodesByXpath(context, xpath, targetName); } if(!ret.found && item._backup) { // we didn't find any nodes matching the xpath query, and // this item has been copied from its original for modification, // so perhaps we explicitly removed it in the modified version for // performance reasons... so lets look it up in the original unmodified version context = item._backup; if(type == "attr") { ret = this._getAttrsByXpath(context, xpath, targetName); } else { ret = this._getNodesByXpath(context, xpath, targetName); } // if ret.found is still false, then just return the defaultValue if (!ret.found) { ret.value = []; } } return ret.value; }, getValueByMapping: function( /* com.ibm.data.AtomEntryItem */ item, /* Object */ mapping, /* Value? */ defaultValue){ // summary: Executes the correct query API using the mapping argument to // retrieve the desired attribute value for the item as specified by the // mapping. // mapping: A mapping object from this object's attrMappings // defaultValue: The default value to return if no value exists for the mapping. // If null is returned by the query, the defaultValue will be returned // instead. var ret = null; var xpath = mapping.xpath; var type = mapping.type; var targetName = mapping.targetName; var context = this._feed; if(item) { this._assertIsItem(item); context = item.element; } if(type == "attr") { ret = this._getAttrByXpath(context, xpath, targetName); } else { ret = this._getNodeByXpath(context, xpath, targetName); } if(!ret.found && item && item._backup) { // we didn't find any nodes matching the xpath query, and // this item has been copied from its original for modification, // so perhaps we explicitly removed it in the modified version for // performance reasons... so lets look it up in the original unmodified version context = item._backup; if(type == "attr") { ret = this._getAttrByXpath(context, xpath, targetName); } else { ret = this._getNodeByXpath(context, xpath, targetName); } // if ret.found is still false, then just return the defaultValue if (!ret.found && defaultValue) { ret.value = defaultValue; } } if (type == "attr" && ret.value && ret.value.length === 0) { ret.value = null; } return ret.value; }, setValueByMapping: function( /* com.ibm.data.AtomEntryItem */ item, /* Object */ mapping, /* Value */ value){ // summary: Executes the correct query API using the mapping argument to // retrieve the desired attribute value for the item as specified by the // mapping. // mapping: A mapping object from this object's attrMappings // value: The value to set that matches the mapping rule on the item. var xpath = mapping.xpath; var type = mapping.type; var targetName = mapping.targetName; var context = this._feed; var ret = false; if(item) { this._assertIsItem(item); context = item.element; } if(type == "attr") { ret = this._setAttrByXpath(context, xpath, targetName, value); } else { ret = this._setNodeByXpath(context, xpath, targetName, value); } if(!ret && mapping.setter) { return mapping.setter.apply(this, arguments); } return ret; }, setValuesByMapping: function( /* com.ibm.data.AtomEntryItem */ item, /* Object */ mapping, /* Array */ values){ // summary: Executes the correct query API using the mapping argument to // retrieve the desired attribute value for the item as specified by the // mapping. // mapping: A mapping object from this object's attrMappings // values: The value to set that matches the mapping rule on the item. var xpath = mapping.xpath; var type = mapping.type; var targetName = mapping.targetName; var context = this._feed; var ret = false; if(item) { this._assertIsItem(item); context = item.element; } if(type == "attr") { ret = this._setAttrsByXpath(context, xpath, targetName, value); } else { ret = this._setNodesByXpath(context, xpath, targetName, value); } if(!ret && mapping.setter) { return mapping.setter.apply(this, arguments); } return ret; }, getAttributes: function(/* com.ibm.data.AtomEntryItem? */ item){ // summary: Gets the valid attributes for the given item if one is provided and // returns an array of attribute names in String format. If the item is // null, it returns an array of attributes on the current feed that can // be retrieved via the getFeedValue function. // item: Optional argument to get the list of attributes from. var attrs = []; var x = null; if(item) { this._assertIsItem(item); for(x in item.attrMappings) { attrs.push(x); } } else { for(x in this.attrMappings) { attrs.push(x); } } return attrs; }, hasAttribute: function( /* com.ibm.data.AtomEntryItem */ item, /* String */ attribute){ // summary: Returns true if the attribute is a valid attribute on the given item, // false otherwise. This does not check if the attribute value on the item // is null or empty, but just checks if the item supports that attribute. // item: Item to check if the attribute is valid // attribute: Name of the attribute to check this._assertIsItem(item); this._assertIsString(attribute); if(attribute.charAt(attribute.length - 1) == '$') { // this looks like an attribute whose map rule is parameterizable // so lets defer checking if its valid until later so we don't // duplicate any string processing return true; } if (item.attrMappings[attribute]) { return true; } else { return false; } }, containsValue: function(/* com.ibm.data.AtomEntryItem */ item, /* String */ attribute, /* Object */ value){ // summary: Returns true if the value of the attribute on the given item is // equal to the value argument, false otherwise. If the attribute is a // multi-valued attribute, it will check all the values of the attribute. // item: Item to check if the attribute is valid // attribute: Name of the attribute to check // value: Value to compare the actual value(s) to. var values = this.getValues(item, attribute); for(var i = 0; i < values.length; i++) { if (values[i] == value) { return true; } } return false; }, isItem: function(/* Object */ something){ // summary: Returns true if the argument is a valid item in this store, false otherwise. // something: Object to check to determine if it is an item in this store. return something.store == this && something.element && !something._isDeleted; }, isItemLoaded: function(/* Object */ something) { // summary: All valid items in this data store are loaded all of the time as there // is no support currently for stubs. Therefore, this is effectively the same // as isItem. return this.isItem(something) && something.loaded; }, loadItem: function(/* Object */ keywordArgs){ // summary: All valid items in this data store are loaded all of the time as there // is no support currently for stubs. Therefore, this function is a no-op. }, getResultSize: function(/*DOMDoc*/ doc, /*Request*/ request, /*Array*/ items) { // summary: Gets the total number of items retrieved by the query, which is not // necessarily the number of items returned to the callbacks to fetch. // This can be used for paging purposes to get the total number of items // on the server that match the query, where subsequent fetch requests can // be made to retrieve additional pages. return items.length; }, processFetchedItems: function(/*Array*/ items, /*Request*/ request) { // summary: Extension point for descendants or consumers of this store to preprocess // the list of items that the store returns from a fetch call. Allows a store // to filter the list or implement caching techniques on the client, or sort // the items, or anything else. // items: Array items of this data store found in the response of a fetch call // returns: Array items of this data store after processing return items; }, processResponse: function(/*DOMDoc*/ xmlDoc, /*Request*/ request) { // summary: Extension point for descendants or consumers of this store to handle // responses from server requests for data. Most subclasses can leave this // function as-is, but if special handling is required that cannot be done // with other extension points, this can be overridden as well. // Must call onBegin, onItem, and onComplete callbacks correctly as // specified by dojo.data.api.Read. The onError callback is handled elsewhere. // xmlDoc: DOMDoc of the response from an IO request // request: Request used to fetch the result if (request.aborted) { return; } var composite = this._parseFeed(request, xmlDoc); var items = composite.items; this._feed = composite.root; items = this.processFetchedItems(items, request); if (request.onBegin) { request.onBegin.call(request.scope, this.getResultSize(composite.root, request, items), request); } if(request.onItem) { for(var i = 0; i < items.length; i++) { if (request.aborted) { return; } request.onItem.call(request.scope, items[i], request); } } if(request.onComplete) { // call onComplete with items array if onItem wasn't specified, otherwise // call it with null if (request.aborted) { return; } if (!request.onItem) { request.onComplete.call(request.scope, items, request); } else { request.onComplete.call(request.scope, null, request); } } }, fetch: function( keywordArgs ) { // summary: // Given a query and set of defined options, such as a start and count of items to return, // this method executes the query and makes the results available as data items. // The format and expectations of stores is that they operate in a generally asynchronous // manner, therefore callbacks are always used to return items located by the fetch parameters. // // description: // A Request object will always be returned and is returned immediately. // The basic request is nothing more than the keyword args passed to fetch and // an additional function attached, abort(). The returned request object may then be used // to cancel a fetch. All data items returns are passed through the callbacks defined in the // fetch parameters and are not present on the 'request' object. // // This does not mean that custom stores can not add methods and properties to the request object // returned, only that the API does not require it. For more info about the Request API, // see dojo.data.api.Request // // keywordArgs: // The keywordArgs parameter may either be an instance of // conforming to dojo.data.api.Request or may be a simple anonymous object // that may contain any of the following: // { // query: query-string or query-object, // queryOptions: object, // onBegin: Function, // onItem: Function, // onComplete: Function, // onError: Function, // scope: object, // start: int // count: int // sort: array // } // All implementations should accept keywordArgs objects with any of // the 9 standard properties: query, onBegin, onItem, onComplete, onError // scope, sort, start, and count. Some implementations may accept additional // properties in the keywordArgs object as valid parameters, such as // {includeOutliers:true}. // // The *query* parameter. // The query may be optional in some data store implementations. // The dojo.data.api.Read API does not specify the syntax or semantics // of the query itself -- each different data store implementation // may have its own notion of what a query should look like. // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data // and dojox.data support an object structure query, where the object is a set of // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the // dijit widgets, such as ComboBox assume this to be the case when working with a datastore // when they dynamically update the query. Therefore, for maximum compatibility with dijit // widgets the recommended query parameter is a key/value object. That does not mean that the // the datastore may not take alternative query forms, such as a simple string, a Date, a number, // or a mix of such. Ultimately, The dojo.data.api.Read API is agnostic about what the query // format. // Further note: In general for query objects that accept strings as attribute // value matches, the store should also support basic filtering capability, such as * // (match any character) and ? (match single character). An example query that is a query object // would be like: { attrFoo: "value*"}. Which generally means match all items where they have // an attribute named attrFoo, with a value that starts with 'value'. // // The *queryOptions* parameter // The queryOptions parameter is an optional parameter used to specify optiosn that may modify // the query in some fashion, such as doing a case insensitive search, or doing a deep search // where all items in a hierarchical representation of data are scanned instead of just the root // items. It currently defines two options that all datastores should attempt to honor if possible: // { // ignoreCase: boolean, //Whether or not the query should match case sensitively or not. Default behaviour is false. // deep: boolean //Whether or not a fetch should do a deep search of items and all child // //items instead of just root-level items in a datastore. Default is false. // } // // The *onBegin* parameter. // function(size, request); // If an onBegin callback function is provided, the callback function // will be called just once, before the first onItem callback is called. // The onBegin callback function will be passed two arguments, the // the total number of items identified and the Request object. If the total number is // unknown, then size will be -1. Note that size is not necessarily the size of the // collection of items returned from the query, as the request may have specified to return only a // subset of the total set of items through the use of the start and count parameters. // // The *onItem* parameter. // function(item, request); // If an onItem callback function is provided, the callback function // will be called as each item in the result is received. The callback // function will be passed two arguments: the item itself, and the // Request object. // // The *onComplete* parameter. // function(items, request); // // If an onComplete callback function is provided, the callback function // will be called just once, after the last onItem callback is called. // Note that if the onItem callback is not present, then onComplete will be passed // an array containing all items which matched the query and the request object. // If the onItem callback is present, then onComplete is called as: // onComplete(null, request). // // The *onError* parameter. // function(errorData, request); // If an onError callback function is provided, the callback function // will be called if there is any sort of error while attempting to // execute the query. // The onError callback function will be passed two arguments: // an Error object and the Request object. // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onComplete, onError, etc) will be invoked in the context of the scope // object. In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global(). // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global(), item, request) // // The *start* parameter. // If a start parameter is specified, this is a indication to the datastore to // only start returning items once the start number of items have been located and // skipped. When this parameter is paired withh 'count', the store should be able // to page across queries with millions of hits by only returning subsets of the // hits for each query // // The *count* parameter. // If a count parameter is specified, this is a indication to the datastore to // only return up to that many items. This allows a fetch call that may have // millions of item matches to be paired down to something reasonable. // // The *sort* parameter. // If a sort parameter is specified, this is a indication to the datastore to // sort the items in some manner before returning the items. The array is an array of // javascript objects that must conform to the following format to be applied to the // fetching of items: // { // attribute: attribute || attribute-name-string, // descending: true|false; // Optional. Default is false. // } // Note that when comparing attributes, if an item contains no value for the attribute // (undefined), then it the default ascending sort logic should push it to the bottom // of the list. In the descending order case, it such items should appear at the top of the list. // // returns: // The fetch() method will return a javascript object conforming to the API // defined in dojo.data.api.Request. In general, it will be the keywordArgs // object returned with the required functions in Request.js attached. // Its general purpose is to provide a convenient way for a caller to abort an // ongoing fetch. // // The Request object may also have additional properties when it is returned // such as request.store property, which is a pointer to the datastore object that // fetch() is a method of. // // exceptions: // Throws an exception if the query is not valid, or if the query // is required but was not supplied. if (!keywordArgs) { keywordArgs = {}; } var me = this; keywordArgs.aborted = false; if (!keywordArgs.scope) { keywordArgs.scope = dojo.global; } var url = this._buildUrl(keywordArgs, this.url); keywordArgs.url = url; var loadHandler = function(response, ioArgs) { if (keywordArgs.aborted) { return; } if (dojo.isIE || window.ActiveXObject !== undefined) { response = com.ibm.domUtilities.docFromString(response); } me.doc = response; me.processResponse(response, keywordArgs); }; var errorHandler = function(error, ioArgs) { if (keywordArgs.onError) { keywordArgs.onError.call(keywordArgs.scope, error, keywordArgs); } }; var preventCache = false; if(keywordArgs.queryOptions && keywordArgs.queryOptions.preventCache) { preventCache = true; } var args = { preventCache: preventCache, content: {}, url: url, handleAs: "xml" } if(keywordArgs.queryOptions && keywordArgs.queryOptions.timeout) { args.timeout = keywordArgs.queryOptions.timeout; } else if(this.timeout) { args.timeout = this.timeout; } if(dojo.isIE || window.ActiveXObject !== undefined) { args.handleAs = "text"; args.content["ibm.web2.contentType"] = "text/xml"; } var handle = null; keywordArgs.abort = function() { keywordArgs.aborted = true; handle.cancel(); }; args = this.modifyRequestArgs(args, keywordArgs); handle = dojo.xhrGet(args); handle.addCallback(loadHandler); handle.addErrback(errorHandler); this._lastRequest = keywordArgs; return keywordArgs; }, modifyRequestArgs: function(ioArgs, request) { // summary: Allows modification to request arguments before sending a request // to the server. Subclasses may override this to modify arguments // like setting headers, manipulating the url, overriding a handleAs argument, // or anything else. Returns the modified request args. // ioArgs: dojo.__XhrArgs request args to be sent to the server // request: dojo.data.api.Request object // returns: dojo.__XhrArgs modified request arguments return ioArgs; }, _queryUrlToObject: function(queryStr) { if (!queryStr || queryStr.length === 0) { return {}; } var start = queryStr.indexOf("?"); var str = queryStr; if(start > -1 && (start < str.length - 1)) { // extract only the query part of the queryStr URL str = queryStr.substring(start + 1); } return dojo.queryToObject(str); }, _prepareQuery: function(keywordArgs) { var query = keywordArgs.query; if (!query) { keywordArgs.query = {}; } else if (dojo.isString(query)) { keywordArgs.query = this._queryUrlToObject(query); } return this.prepareQuery(keywordArgs); }, prepareQuery: function(keywordArgs) { // summary: User overridable function to modify the keywordArgs object // used to fetch results from the back end after the keywordArgs.query // property has been normalized to an object map but before any other // processing has taken place to build the URL. This can be used to // extract other parts of the keywordArgs map to add properties to the // query object that are dependent on the back end implementation. One // example includes extracting the keywordArgs.sort parameters and appending // appropriate properties to the keywordArgs.query property that will end // up being built into the generated URL to the server. // returns: Object Modified keywordArgs object. return keywordArgs; }, _buildUrl: function(keywordArgs, baseUrl) { // summary: Builds a url string from this store's base url and the // query argument. // keywordArgs: Object // A request object map as sent to the fetch function // returns: String The constructed url. var ret = ""; var keywordArgs = this._prepareQuery(keywordArgs); var queryPart = dojo.objectToQuery(keywordArgs.query); if (baseUrl.indexOf("?") > -1) { ret = baseUrl + "&" + queryPart; // String } else { ret = baseUrl + "?" + queryPart; // String } return (this.modifyUrl(keywordArgs, ret)); }, modifyUrl: function(keywordArgs, url) { return url; }, _parseFeed: function(request, xmlDoc) { var items = []; var root = null; var result = this._getNodeByXpath(xmlDoc, "/atom:feed"); if(!result.found) { result = this._getNodeByXpath(xmlDoc, "/atom:entry"); if(!result.found) { // the root is not an atom:feed or atom:entry throw new Error("Illegal atom feed format: the root element is not an ATOM feed or entry element."); } else { // the root is an atom:entry element, so there's only one item root = result.value; var query = this._queryUrlToObject(request.url); items.push(this._getItem({element: root, query: query})); } } else { // the root is an atom:feed element root = result.value; var query = this._queryUrlToObject(request.url); result = this._getNodesByXpath(root ? root : xmlDoc, "./atom:entry"); if(result.found) { var nodes = result.value; for(var i = 0; i < nodes.length; i++) { items.push(this._getItem({element: nodes[i], query: query})); } } } return {items: items, root: root}; }, getFeatures: function () { // summary: // Return supported data APIs // returns: // Map of supported data APIs with keys as API classes and values as true/false to indicate support // by this store. return this._features; //returns Map }, close: function(/*dojo.data.api.Request || keywordArgs || null */ request){ if (request && request.abort) { request.abort(); } }, getLabel: function(/* com.ibm.data.AtomEntryItem */ item){ return this.getEPResolvedValue(item, "title"); }, getLabelAttributes: function(/* com.ibm.data.AtomEntryItem */ item){ return ['title']; }, /********************************/ /** **/ /** **/ /** dojo.data.api.Write **/ /** **/ /** **/ /********************************/ newItem: function(/* Object? */ keywordArgs, /*Object?*/ parentInfo){ // summary: Must be overridden by stores that extend this but support writes, // like ATOM services that support APP. return null; }, deleteItem: function(/* com.ibm.data.AtomEntryItem */ item){ // summary: Deletes an item from the store. Does not persist to the server until save is called. // item: The item to delete. // returns: Boolean true if successful, false otherwise. if (!this._deletedItems) { this._deletedItems = []; } item.backup(); this._deletedItems.push(item); // storing identity into item since we need it later and can't access it through store APIs // after it's been marked as deleted item.id = this.getIdentity(item); item._isDeleted = true; // we are assuming that order does not matter, so we don't have to save the item's // current position in the list of entries in this feed return true; }, setValue: function( /* com.ibm.data.AtomEntryItem */ item, /* String */ attribute, /* almost anything */ value){ var ret = false; var oldValue = null; if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings); // if mapRule is null here, then we didn't find one // in the map, so we can't proceed successfully if(mapRule) { item.backup(); oldValue = this.getValueByMapping(item, mapRule); ret = this.setValueByMapping(item, mapRule, value); if(ret) { this._addDirtyItem(item); item.logWrite({attribute: attribute, value: value}); } } } else { throw new Error(attribute + " is not an attribute of any item in this store"); } return {success: ret, oldValue: oldValue, newValue: value}; }, setValues: function(/* com.ibm.data.AtomEntryItem */ item, /* String */ attribute, /* array */ values){ var ret = false; var oldValue = null; if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings, true); // if mapRule is null here, then we didn't find one // in the map, so we can't proceed successfully if(mapRule) { item.backup(); oldValue = this.getValueByMapping(item, mapRule); ret = this.setValuesByMapping(item, mapRule, values); if(ret) { this._addDirtyItem(item); item.logWrite({attribute: attribute, values: values}); } } } else { throw new Error(attribute + " is not an attribute of any item in this store"); } return {success: ret, oldValue: oldValue, newValue: values}; }, unsetAttribute: function(/* com.ibm.data.AtomEntryItem */ item, /* String */ attribute){ var ret = false; var oldValue = null; var value = null; if(this.hasAttribute(item, attribute)) { var mapRule = this.getMapRule(attribute, item.attrMappings); // if mapRule is null here, then we didn't find one // in the map, so we can't proceed successfully if(mapRule) { item.backup(); oldValue = this.getValueByMapping(item, mapRule); if(mapRule.type == "attr") { ret = this.setValueByMapping(item, mapRule, ""); value = ""; } else { // it's a node or node property if (mapRule.targetName) { ret = this.setValueByMapping(item, mapRule, null); } // if targetName, then it's a property else if (oldValue) { // if no targetName, then it's a node... so remove the node oldValue.parentNode.removeChild(oldValue); ret = true; } } if(ret) { this._addDirtyItem(item); item.logWrite({attribute: attribute, value: value}); } } } return {success: ret, oldValue: oldValue, newValue: value}; }, save: function(/* Object */ keywordArgs){ var it = null; for(var d = 0; d < this._deletedItems.length; d++) { it = this._deletedItems[d]; it.persist(); delete it.element; } for(var i = 0; i < this._dirtyItems.length; i++) { this._dirtyItems[i].persist(); } for(var i = 0; i < this._newItems.length; i++) { this._newItems[i].persist(); } // the persist operation of the above items set some values on their backups // which may have updated the dirtyItems list... by putting this below here // we're removing those immediately if they are here // instead of just reinitializing these pointers, // splice these so that we can keep the same array references forever in case others have // pointers to these still (so that they won't break) this._newItems.splice(0, this._newItems.length); this._dirtyItems.splice(0, this._dirtyItems.length); this._deletedItems.splice(0, this._deletedItems.length); }, _addDirtyItem: function(/* com.ibm.portal.data.ContentModelItem */ item) { if (item._isDirty) { return; } item._isDirty = true; // need to check this for(var i = 0; i < this._dirtyItems.length; i++) { if (this._dirtyItems[i] == item) { return; } } this._dirtyItems.push(item); }, revert: function(){ var it = null; for(var d = 0; d < this._deletedItems.length; d++) { it = this._deletedItems[d]; it.restore(); delete item._isDeleted; } for(var i = 0; i < this._dirtyItems.length; i++) { this._dirtyItems[i].restore(); } this._dirtyItems = []; this._deletedItems = []; }, isDirty: function(/* com.ibm.data.AtomEntryItem? */ item){ // summary: Determines whether or not an item is dirty or if this store in general has dirty items. // If item is specified, it returns true if that item is dirty, and false otherwise. // If no item is specified, it returns an array of all the dirty items in this store (modified, // deleted, and new), and false if there are none. // item: Optional argument to check if a particular item is dirty. Leaving this null if (item && this._assertIsItem(item)) { return item._isDirty; } else { var array = this._dirtyItems.concat(this._deletedItems).concat(this._newItems); if (!array || array.length === 0) { return false; } else { return array; } } } } ); dojo.declare( "com.ibm.data.AtomEntryItem", com.ibm.data.XpathHelper, { namespaces: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.namespaces), // attrMappings: This maps attribute names to a query object consisting of an // xpath expression used to find the relevant node(s), an attr name, and a type. // The map contains key-value pairs in this format: // { // attributeName: {xpath: "./this/is/the/xpath/expr", targetName: "attrName"|null, type: "node"|"attr"} // } // The xpath expression is resolved from the context of the atom:entry node // assigned to this atom feed entry item. The attr property is an optional property // used to specify whether to get an object property of the returned DOM node object or // an attribute of the returned DOM node. The type specifies what type the lookup // will perform in conjunction with the attr. If the lookup is an attribute of the // DOM node, it will attempt to resolve any namespacing it encounters when looking // up the attribute. The following explains the possible combinations of lookup // mechanisms. // // xpath: "./node", targetName: "textContent", type: "node" = node["textContent"] // xpath: "./node", targetName: "href", type: "attr" = node.getAttribute("href") // xpath: "./node", targetName: "textContent", type: "node" = cross browser node["textContent"] property // xpath: "./node", targetName: null, type: "node" = node // xpath: "./node", targetName: null, type: "attr" = invalid query, requires the attribute name // // Map rules can be parameterized by substituting parameters specified by the caller into the // rule template. Parameterized map rules MUST end with a $ character to mark them as such. // Example: // pAttribute$: {xpath: "./${0}['${1}']", targetName: "${2}", type: "node", customRuleProperty: "${1}"} // // Callers supply a comma-separated list of parameter values to be bound to subsequent numerical // parameter keys in the template respectively surrounded by brackets immediately following the // name of the attribute key but before the $ character. // Example: // To request the value of pAttribute$ with values set for the parameters, the caller would // use code like this - // store.getValue(item, "pAttribute[div, id, nodeValue]$"); // attrMappings: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.attrMappings, { subtitle: {xpath: "./atom:subtitle", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:subtitle"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, generator: {xpath: "./atom:generator", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:generator"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, icon: {xpath: "./atom:icon", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:icon"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } }, logo: {xpath: "./atom:logo", targetName: "textContent", type: "node", setter: function(item, rule, value){ var elem = this._createNSElement(item.element.ownerDocument, "atom:logo"); this._setNodeProp(elem, rule.targetName, value); item.element.appendChild(elem); return true; } } }), constructor: function( /*Map*/ args ) { dojo.mixin(this, args); this._writeLog = []; }, toString: function() { return "<" + this.__getClassName() + ">"; }, _onCreate: function() { // summary: Called internally to indicate an item has been created. Should // be called immediately on creation for stores that do not save to // a backend server, but should be called on a valid server response for // an item that was created and saved to the server successfully. This // calls onCreate to notify listeners of the event and can therefore be // overriden if custom logic needs to be applied to handle cases where // other conditions need to be satisfied before the onCreate event is called. this.onCreate(); }, onCreate: function() { // summary: Called when this item is created. This callback is reserved // for sending notifications when a new item stub has been created // on the actual server. This will be called when a new item has been // created, saved to the server, and the server responds back with // a verification of the created item. Some stores may not have any // notion of creating items on the server and should call this immediately // when the item is created on the client, while others actually defer // the full creation of a new item from the newItem call until after the // save operation has persisted to the backend. This allows interested // parties to get notified when an item they've created is actually saved // on the server. This is especially useful if the server has to generate // something like a unique ID for the item which is not available on the // item until the server response comes back. }, _makeModifiable: function() { // summary: Modifies the element of this item to become modifiable // for writing back to the server. Subclasses can override this // to do things like prune an item to only include necessary information // for updates and nothing more. This is called by backup once after // backing up the original element first. After this returns, // this item's element may have been modified. Any queries against // this item should now include logic to check the backup copy as well // if a query doesn't return anything as it could have been deleted by // this function. Care should be taken to ensure that the query result // specifically detects if nothing was found or if the actual value was // null or empty. }, backup: function() { // summary: Creates a single backup copy of this item on the client for // later restoration if necessary. Calling this function multiple // times before restore() or persist() is called on this item will do nothing // after the initial backup, effectively maintaining only the first // version of the item before any changes were made. if(!this._backup) { this._backup = this.element.cloneNode(true); this._makeModifiable(); this._logEnabled = true; return true; } return false; }, restore: function() { // summary: Restores the single backup copy of this item on the client, // erasing all modifications made since backup() was called. if(!this._backup) { dojo.place(this._backup, this.element, "before"); this.element.parentNode.removeChild(this.element); delete this.element; this.element = this._backup; this.backup = null; this._writeLog.splice(0, this._writeLog.length); return true; } return false; }, persist: function() { // summary: Persists the changes made to this item since the backup() was // called. After this is called, there is no way to restore the backup // as it is erased completely. this._writeLogToBackup(); this._isDirty = false; delete this._backup; }, logWrite: function(map) { // summary: Logs write operations to this item so that they can later // be repeated to the backup during persistence. if (this._logEnabled) { this._writeLog.push(map); } }, _writeLogToBackup: function() { // summary: Repeates the write log for this item to the original backup // node. Used mainly for persisting changes to support a 2-phase // write operation (write, commit). if(this._backup) { this.element = this._backup; this._logEnabled = false; for(var i = 0; i < this._writeLog.length; i++) { var op = this._writeLog[i]; if(op.values) { this.store.setValues(this, op.attribute, op.values); } else { this.store.setValue(this, op.attribute, op.value); } } this._writeLog.splice(0, this._writeLog.length); } } } ); dojo.declare( "com.ibm.data.AtomPersonConstruct", com.ibm.data._XmlConstruct, { // summary: Construct for retrieving appropriate information // from an Atom Person Construct as defined in // http://tools.ietf.org/html/rfc4287#section-3.2. namespaces: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.namespaces), constructor: function(/*DOMNode*/ root) { this.root = root; }, getName: function() { // summary: Gets the name specified by this Atom person construct. // returns: String var result = this._getNodeByXpath(this.root, "./atom:name", "textContent"); if (!result.found) { result.value = ""; } return result.value; }, getEmail: function() { // summary: Gets the email specified by this Atom person construct. // returns: String var result = this._getNodeByXpath(this.root, "./atom:email", "textContent"); if (!result.found) { result.value = ""; } return result.value; }, getURI: function() { // summary: Gets the name specified by this Atom person construct. // returns: String var result = this._getNodeByXpath(this.root, "./atom:uri", "textContent"); if (!result.found) { result.value = ""; } return result.value; }, toString: function() { // summary: Gets the name string specified by this Atom person construct. // returns: String return this.getName(); } } ); dojo.declare( "com.ibm.data.AtomDateConstruct", com.ibm.data._XmlConstruct, { // summary: Construct for retrieving javascript-appropriate information // from an Atom Date Construct as defined in // http://tools.ietf.org/html/rfc4287#section-3.3. namespaces: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.namespaces), // Supports date strings in the following formats - // 2003-12-13T18:30:02Z // 2003-12-13T18:30:02.25Z // 2003-12-13T18:30:02+01:00 // 2003-12-13T18:30:02.25+01:00 constructor: function(/*DOMNode*/ root) { this.root = root; }, getDate: function() { // summary: Parses and retrieves a javascript Date object from a date string // specified in an Atom feed according to the allowable string formats // by the Atom spec itself (http://tools.ietf.org/html/rfc4287#section-3.3) // returns: Date if (this.date) { return this.date; } var d = this._getNodeProp(this.root, "textContent"); if (!d) { return null; } this.date = dojo.date.stamp.fromISOString(d); return this.date; }, getTime: function() { // summary: Gets the time specified by this Atom date construct. // returns: int this.getDate(); if (this.date) { return this.date.getTime(); } return null; }, toString: function() { // summary: Gets a string representation of the date specified by this // Atom data construct. this.getDate(); if (this.date) { return this.date.toString(); } return ""; } } ); } if(!dojo._hasResource["com.ibm.data.OpenSearchFeedStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.OpenSearchFeedStore"] = true; dojo.provide( "com.ibm.data.OpenSearchFeedStore" ); dojo.require( "com.ibm.data.AtomFeedStore" ); dojo.require( "dojox.data.dom" ); dojo.declare( "com.ibm.data.OpenSearchFeedStore", com.ibm.data.AtomFeedStore, { // namespaces: Map // Map of namespaces used by the MashupHub feeds namespaces: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.namespaces, { "xhtml" : "http://www.w3.org/1999/xhtml", "os" : "http://a9.com/-/spec/opensearch/1.1/" }), attrMappings: dojo.mixin({}, com.ibm.data.AtomFeedStore.prototype.attrMappings, { nextLink: {xpath: "./atom:link[@rel='next']", targetName: "href", type: "attr"}, startIndex: {xpath: "./os:startIndex", targetName: "textContent", type: "node"}, itemsPerPage: {xpath: "./os:itemsPerPage", targetName: "textContent", type: "node"}, totalResults: {xpath: "./os:totalResults", targetName: "textContent", type: "node"}, query: {xpath: "./os:Query", type: "OpenSearchQueryConstruct"} }), // itemClass: String the class used to represent items of this store itemClass: "com.ibm.data.OpenSearchFeedItem", constructor: function( /*Map*/ args ) { // summary: Creates a new instance of a com.ibm.portal.data._PortalModelStore // args: Map // The args map will be used to replace any default values of this store with the // values specified in the args map. this._features['dojo.data.api.Identity'] = true; }, handleConstructs: function(/*DOMNode*/ node, /*String*/ prop) { switch(prop) { case "OpenSearchQueryConstruct": return new com.ibm.data.OpenSearchQueryConstruct(node); default: return this.inherited(arguments); } }, getResultSize: function(/*DOMDoc*/ doc, /*Request*/ request, /*Array*/ items) { // summary: Gets the total number of items retrieved by the query, which is not // necessarily the number of items returned to the callbacks to fetch. // This can be used for paging purposes to get the total number of items // on the server that match the query, where subsequent fetch requests can // be made to retrieve additional pages. var rule = this.attrMappings.totalResults; var ret = null; if(rule.type == "attr") ret = this._getAttrByXpath(doc, rule.xpath, rule.targetName); else ret = this._getNodeByXpath(doc, rule.xpath, rule.targetName); // if we found total results, return it as a number if(ret.found) return ret.value - 0; // otherwise, return -1 to indicate that we don't know else return -1; }, /********************************/ /** **/ /** **/ /** dojo.data.api.Identity **/ /** **/ /** **/ /********************************/ fetchItemByIdentity: function(/* object */ keywordArgs){ // summary: // Given the identity of an item, this method returns the item that has // that identity through the onItem callback. Conforming implementations // should return null if there is no item with the given identity. // Implementations of fetchItemByIdentity() may sometimes return an item // from a local cache and may sometimes fetch an item from a remote server, // // keywordArgs: // An anonymous object that defines the item to locate and callbacks to invoke when the // item has been located and load has completed. The format of the object is as follows: // { // identity: string|object, // onItem: Function, // onError: Function, // scope: object // } // The *identity* parameter. // The identity parameter is the identity of the item you wish to locate and load // This attribute is required. It should be a string or an object that toString() // can be called on. // // The *onItem* parameter. // Function(item) // The onItem parameter is the callback to invoke when the item has been loaded. It takes only one // parameter, the item located, or null if none found. // // The *onError* parameter. // Function(error) // The onError parameter is the callback to invoke when the item load encountered an error. It takes only one // parameter, the error object // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onError, etc) will be invoked in the context of the scope object. // In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global. // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global, item, request) return false; } } ); dojo.declare( "com.ibm.data.OpenSearchFeedItem", com.ibm.data.AtomEntryItem, { // namespaces: Map // Map of namespaces used by the MashupHub feeds namespaces: dojo.mixin({}, com.ibm.data.OpenSearchFeedStore.prototype.namespaces) } ); dojo.declare( "com.ibm.data.OpenSearchQueryConstruct", com.ibm.data._XmlConstruct, { namespaces: dojo.mixin({}, com.ibm.data.OpenSearchFeedStore.prototype.namespaces), constructor: function(/*DOMNode*/ root) { this.root = root; }, getRole: function() { return this._getAttribute(this.root, "role"); }, getTitle: function() { return this._getAttribute(this.root, "title"); }, getTotalResults: function() { return this._getAttribute(this.root, "totalResults"); }, getSearchTerms: function() { return this._getAttribute(this.root, "searchTerms"); }, getCount: function() { return this._getAttribute(this.root, "count"); }, getStartIndex: function() { return this._getAttribute(this.root, "startIndex"); }, getStartPage: function() { return this._getAttribute(this.root, "startPage"); }, getLanguage: function() { return this._getAttribute(this.root, "language"); }, getInputEncoding: function() { return this._getAttribute(this.root, "inputEncoding"); }, getOutputEncoding: function() { return this._getAttribute(this.root, "outputEncoding"); }, toString: function() { return com.ibm.domUtilities.stringFromDoc(this.root); } } ); } if(!dojo._hasResource["com.ibm.data.MashupHubStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.MashupHubStore"] = true; dojo.provide( "com.ibm.data.MashupHubStore" ); dojo.require( "com.ibm.data.ProxyHelper" ); dojo.declare( "com.ibm.data.MashupHubStore", [com.ibm.data.OpenSearchFeedStore, com.ibm.data.ProxyHelper], { // namespaces: Map // Map of namespaces used by the MashupHub feeds namespaces: null, // itemClass: String the class used to represent items of this store itemClass: "com.ibm.data.MashupHubItem", constructor: function( /*Map*/ args ) { // summary: Creates a new instance of a this data store // args: Map // The args map will be used to replace any default values of this store with the // values specified in the args map. this._features['dojo.data.api.Identity'] = true; var newUrl = new com.ibm.mm.enabler.utils.HttpUrl(this.url); this.url = newUrl.toProxifiedString(); this.bundle = dojo.i18n.getLocalization("com.ibm.bundles", "Shelf"); if(args && args.namespace == 'widget') { this.namespaces = dojo.delegate(com.ibm.data.OpenSearchFeedStore.prototype.namespaces,{ catalog: "http://www.ibm.com/xmlns/atom/opensearch/widget/1.0/" }); } else if(args && args.namespace == 'feed') { this.namespaces = dojo.delegate(com.ibm.data.OpenSearchFeedStore.prototype.namespaces,{ catalog: "http://www.ibm.com/xmlns/atom/opensearch/feed/1.0/" }); } else { this.namespaces = dojo.delegate(com.ibm.data.OpenSearchFeedStore.prototype.namespaces,{ catalog: "http://www.ibm.com/opensearch/1.0" }); } }, handleConstructs: function(/*DOMNode*/ node, /*String*/ prop) { switch(prop) { case "OpenSearchQueryConstruct": return new com.ibm.data.MashupHubQueryConstruct(node); default: return this.inherited(arguments); } }, fetchItemByIdentity: function(/* object */ keywordArgs){ // summary: // Given the identity of an item, this method returns the item that has // that identity through the onItem callback. Conforming implementations // should return null if there is no item with the given identity. // Implementations of fetchItemByIdentity() may sometimes return an item // from a local cache and may sometimes fetch an item from a remote server, // // keywordArgs: // An anonymous object that defines the item to locate and callbacks to invoke when the // item has been located and load has completed. The format of the object is as follows: // { // identity: string|object, // onItem: Function, // onError: Function, // scope: object // } // The *identity* parameter. // The identity parameter is the identity of the item you wish to locate and load // This attribute is required. It should be a string or an object that toString() // can be called on. // // The *onItem* parameter. // Function(item) // The onItem parameter is the callback to invoke when the item has been loaded. It takes only one // parameter, the item located, or null if none found. // // The *onError* parameter. // Function(error) // The onError parameter is the callback to invoke when the item load encountered an error. It takes only one // parameter, the error object // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onError, etc) will be invoked in the context of the scope object. // In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global. // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global, item, request) var id = keywordArgs.identity; // our IDs may come in as urn:id:# and all we want is the # part if(id.indexOf(":") > -1) id = id.substring(id.lastIndexOf(":") + 1); var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global; var args = { query: { id: id, collection: "ui.all" }, onComplete: dojo.hitch(this, function(items) { if(items.length == 0 && keywordArgs.onError) { keywordArgs.onError.call(scope, "No items returned matching identity " + id); } else { keywordArgs.onItem.call(scope, items[0]); } } ) }; if(keywordArgs.onError) args.onError = dojo.hitch(scope, keywordArgs.onError); return this.fetch(args); } } ); dojo.declare( "com.ibm.data.MashupHubItem", com.ibm.data.OpenSearchFeedItem, { // namespaces: Map // Map of namespaces used by the MashupHub feeds namespaces: dojo.mixin({}, com.ibm.data.MashupHubStore.prototype.namespaces), attrMappings: dojo.mixin({}, com.ibm.data.OpenSearchFeedItem.prototype.attrMappings, { editLink: {xpath: "./atom:link[@rel='self']", targetName: "href", type: "attr"}, category: {xpath: "./atom:category", targetName: "term", type: "attr"}, catalogVersion: {xpath: "./atom:content/catalog:*/catalog:version", targetName: "textContent", type: "node"}, catalogName: {xpath: "./atom:content/catalog:*/catalog:name", targetName: "textContent", type: "node"}, catalogAuthor: {xpath: "./atom:content/catalog:*/catalog:author", targetName: "textContent", type: "node"}, catalogDescription: {xpath: "./atom:content/catalog:*/catalog:description", targetName: "textContent", type: "node"}, catalogRating: {xpath: "./atom:content/catalog:*/catalog:rating", targetName: "textContent", type: "node"}, catalogUseCount: {xpath: "./atom:content/catalog:*/catalog:useCount", targetName: "textContent", type: "node"}, catalogDateModified: {xpath: "./atom:content/catalog:*/catalog:dateModified", targetName: "textContent", type: "node"}, catalogNumComments: {xpath: "./atom:content/catalog:*/catalog:numComments", targetName: "textContent", type: "node"}, catalogTags: {xpath: "./atom:content/catalog:*/catalog:tags/catalog:tags", targetName: "textContent", type: "node"}, catalogCategories: {xpath: "./atom:content/catalog:*/catalog:categories", targetName: "textContent", type: "node"}, catalogDocumentationURL: {xpath: "./atom:content/catalog:*/catalog:documentationURL", targetName: "textContent", type: "node"}, catalogDownloadURL: {xpath: "./atom:content/catalog:*/catalog:downloadURL", targetName: "textContent", type: "node"}, catalogIcon: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='icon']", targetName: "value", type: "attr"}, catalogWidgetParam: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='widget.metadata.widgetParam']", targetName: "value", type: "attr"}, catalogOnNewWire: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='widget.metadata.onNewWire']", targetName: "value", type: "attr"}, catalogAutoWire: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='widget.metadata.com.ibm.mashups.builder.autoWiringEnabled']", targetName: "value", type: "attr"}, catalogPermission: {xpath: "./atom:content/catalog:*/catalog:permission", targetName: "textContent", type: "node"}, catalogDefinitionURL: {xpath: "./atom:content/catalog:*/catalog:definitionURL", targetName: "textContent", type: "node"}, catalogTitles: {xpath: "./atom:content/catalog:*/catalog:l10n/catalog:title", targetName: null, type: "node"}, catalogSandboxUser: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='sandbox_user']", targetName: "value", type: "attr"}, catalogSandboxUserDefinition: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='widget.metadata.sandbox_definition']", targetName: "value", type: "attr"}, catalogWidgetType: {xpath: "./atom:content/catalog:*/catalog:objectmeta/catalog:metadata[@name='widget.metadata.widgetType']", targetName: "value", type: "attr"} }) } ); dojo.declare( "com.ibm.data.MashupHubQueryConstruct", com.ibm.data.OpenSearchQueryConstruct, { namespaces: dojo.mixin({}, com.ibm.data.MashupHubStore.prototype.namespaces), getSearchType: function() { return this._getAttribute(this.root, "catalog:searchType"); }, getCollection: function() { return this._getAttribute(this.root, "catalog:collection"); } } ); } if(!dojo._hasResource["com.ibm.data.MashupFileDataStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.data.MashupFileDataStore"] = true; dojo.provide("com.ibm.data.MashupFileDataStore"); dojo.require( "com.ibm.data.CatalogMixin" ); dojo.declare("com.ibm.data.MashupFileDataStore", [com.ibm.data.MashupHubStore,com.ibm.data.CatalogMixin], { /********************************/ /** **/ /** **/ /** catalog extension point **/ /** **/ /** **/ /********************************/ mapItem: function(/*item*/item){ var map = {}; map.id = this.getEPResolvedValue(item, "id", null); map.label = this.getEPResolvedValue(item, "catalogName", this.bundle["shelf_untitled"]); map.description = this.getEPResolvedValue(item, "catalogDescription", ""); map.tags = this.getEPResolvedValue(item, "catalogTags", ""); map.rating = this.getEPResolvedValue(item, "catalogRating", 0.0); map.url = this.getEPResolvedValue(item, "catalogDownloadURL", ""); map.type = this.getEPResolvedValue(item, "category", ""); map.thumbnail = this.getValue(item, "catalogIcon", ""); map.widgetParam = this.getEPResolvedValue(item, "catalogWidgetParam", ""); map.onNewWire = this.getEPResolvedValue(item, "catalogOnNewWire", ""); map.onAutoWire = this.getEPResolvedValue(item, "catalogAutoWire", ""); map.permission = this.getEPResolvedValue(item, "catalogPermission", ""); map.definitionURL = this.getValue(item, "catalogDefinitionURL", ""); map.sandbox_user = this.getEPResolvedValue(item, "catalogSandboxUser", "false"); map.sandbox_definition = this.getEPResolvedValue(item, "catalogSandboxUserDefinition", "false"); map.icon = this.getValue(item, "catalogIcon", "false"); map.widgetType = this.getEPResolvedValue(item, "catalogWidgetType", ""); if (map.widgetType === "mm_OSGadget") { map.titles = this.getValue(item, "catalogTitles", null); } map.hubURL = this.url; map.isTrusted = map.sandbox_user == "true" ? false : true; if(map.widgetParam.length > 0) { map.prefsMapFcn = this.prefsMapFcn; } if (!map.thumbnail && builderConfig.builderPath) { if (item.store.namespace == "widget"){ map["thumbnail"] = builderConfig.builderPath + "/images/generic_widget_icon.png"; } else { map["thumbnail"] = builderConfig.builderPath + "/images/gray_generic_feed_icon.png"; } } return map; }, prefsMapFcn: function(map){ var pars = dojo.fromJson(map.widgetParam); var ret = {}; for(var i in pars){ ret[pars[i].itemName] = pars[i].itemValue; } return ret; }, prepareQuery: function(keywordArgs) { if(keywordArgs.query.keywords) { keywordArgs.query.searchTerms = keywordArgs.query.keywords.join(" "); keywordArgs.query.searchType = ""; delete keywordArgs.query.keywords; } else if(keywordArgs.query.tags && keywordArgs.query.tags.length > 0) { keywordArgs.query.searchTerms = keywordArgs.query.tags[0]; keywordArgs.query.searchType = "tag"; delete keywordArgs.query.tags; } else { keywordArgs.query.searchTerms = "*"; keywordArgs.query.searchType = ""; } if(keywordArgs.sort && keywordArgs.sort.length > 0) { switch(keywordArgs.sort[0].attribute) { case "date": keywordArgs.query.sortBy = "updated"; break; default: keywordArgs.query.sortBy = keywordArgs.sort[0].attribute; } keywordArgs.query.sortOrder = "asc"; if(keywordArgs.sort[0].descending == true) { keywordArgs.query.sortOrder = "desc"; } } if(keywordArgs.count) { keywordArgs.query.count = keywordArgs.count; if(keywordArgs.start == null) { keywordArgs.start = 0; } keywordArgs.query.startIndex = keywordArgs.start; } // NEVER get the feed from the cache, defect 16700 if(!keywordArgs.queryOptions){ keywordArgs.queryOptions = {}; } keywordArgs.queryOptions.preventCache = true; return keywordArgs; }, validateSelf: function(){ // summary: validates this datastore, if it does not validate the source will not be used // returns true for validation and false otherwise var configSvc = com.ibm.mashups.services.ServiceManager.getService(com.ibm.mashups.enabler.services.ConfigService.SERVICE_NAME); var hubUrl = configSvc.getValue(com.ibm.mashups.enabler.services.ConfigConstants.HUB_URL); if(dojo.trim(ibmConfig["com.ibm.mashups.builder.contentShelf.mode"]) != "mashups" && hubUrl && this.url.indexOf("undefined") < 0) { return true; } else { return false; } } }); } if(!dojo._hasResource["com.ibm.pb.data.InstalledPortletStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.pb.data.InstalledPortletStore"] = true; dojo.provide( "com.ibm.pb.data.InstalledPortletStore" ); dojo.require("dojo.data.util.simpleFetch"); dojo.require( "com.ibm.data.AtomFeedStore" ); dojo.require( "com.ibm.data.CatalogMixin" ); dojo.declare( "com.ibm.pb.data.InstalledPortletStore", [com.ibm.data.AtomFeedStore,com.ibm.data.CatalogMixin], { // itemClass: String the class used to represent items of this store itemClass: "com.ibm.pb.data.InstalledPortletItem", _loadFinished: false, _items: [], noIcons: true, idPrefix: "", constructor: function( /*Map*/ args ) { }, /********************************/ /** **/ /** **/ /** catalog extension point **/ /** **/ /** **/ /********************************/ mapItem: function(/*DatstoreItem*/item){ var map = {}; var id = this.getEPResolvedValue(item, "id", ""); map.id = id.indexOf(this.idPrefix) == 0 ? id.substring(this.idPrefix.length) : id; map.label = this.getEPResolvedValue(item, "title", "untitled"); map.description = this.getEPResolvedValue(item, "summary", ""); return map; }, _fetchItems: function(/*Object*/ keywordArgs, /*Function*/ findCallback, /*Function*/ errorCallback){ // summary: see dojo.data.util.simpleFetch //The _fetchItems() method should ignore any keywordArgs parameters for start, count, onBegin, onItem, onComplete, onError, sort, and scope, and only needs to handle queryOptions and query var me = this; var filter = function(requestArgs, arrayOfItems){ var items = []; if(requestArgs.query){ var keywordStrings = requestArgs.query.keywords; var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false; // loop through the keywordStrings and push anything that isn't an empty string onto the keywords array var keywords = []; for(var i = 0; i < keywordStrings.length; i++){ if(keywordStrings[i].length <= 0) { continue; } // make the keyword all lower case if we are doing a case insensitive search var keyword = (ignoreCase)?keywordStrings[i].toLowerCase():keywordStrings[i]; keywords.push(keyword); } // loop through all the items in the store looking for matches for(var i = 0; i < arrayOfItems.length; ++i){ var match = true; if(arrayOfItems[i] === null){ match = false; } else { // loop through each keyword and make sure the store item contains all of them to be a match for(var j = 0; j < keywords.length; j++){ var title = me.getValue(arrayOfItems[i],"title",""); if(ignoreCase) { title = title.toLowerCase(); } // make the title all lower case if we are doing a case insensitive search var summary = me.getValue(arrayOfItems[i],"summary",""); if(ignoreCase) { summary = summary.toLowerCase(); } // make the summary all lower case if we are doing a case insensitive search // if the title AND summary both don't contain the keyword, the match is false if(title.indexOf(keywords[j]) == -1 && summary.indexOf(keywords[j]) == -1){ match = false; break; } } } if(match) { items.push(arrayOfItems[i]); } } findCallback(items, requestArgs); } else { findCallback(arrayOfItems, requestArgs); } }; if(this._loadFinished){ filter(keywordArgs, this._items); } else { // the items in the store must be fetched and stored for the first and last time var args = arguments; // create a reference to arguments var savedOnComplete = arguments[0].onComplete; // save a reference to arguments' onComplete function // create a new onComplete function to pass to the inherited fetch function args[0].onComplete = function(items){ args[0].onComplete = savedOnComplete; // restore the onComplete function for _fetchItems me._loadFinished = true; me._items = items; // store the items in the store filter(keywordArgs,items); }; // retrieve all the store items using the inherited fetch function com.ibm.data.AtomFeedStore.prototype.fetch.apply(this, args); } } }); dojo.extend(com.ibm.pb.data.InstalledPortletStore,dojo.data.util.simpleFetch); dojo.declare( "com.ibm.pb.data.InstalledPortletItem", com.ibm.data.AtomEntryItem, { } ); } if(!dojo._hasResource["com.ibm.pb.data.LayoutTemplateModelStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.pb.data.LayoutTemplateModelStore"] = true; dojo.provide( "com.ibm.pb.data.LayoutTemplateModelStore" ); dojo.require( "com.ibm.data.CatalogMixin" ); // summary: Wrappers Enabler's Layout Template Model API into a dojo DataStore for use with dojo widgets. // description: This data store in particular is designed to work with the Customize shelf widgets. dojo.declare( "com.ibm.pb.data.LayoutTemplateModelStore", [com.ibm.data.CatalogMixin], { constructor: function () { this.model = com.ibm.mashups.enabler.layouttemplate.Factory.getTemplateModel(); this.localizedContext = com.ibm.mashups.enabler.context.Factory.getLocalizedContext(); }, searchable: true, mapItem: function(/*DatstoreItem*/item){ // summary: Returns a map containing parameters for the given datastore item // The keys must be: label, description, tags, rating, url, id, thumbnail // - label, description, url, id and thumbnail are strings // - tags is an array of strings // - rating is a string that contains a number, note that decimal values are allowed // Having all content search datastores create item maps with the same keys allows the widget to access information uniformly // If a datastore item does not have the same attributes for getValue as seen below, this function must be overridden // If a datastore item does not have an appropriate parameter to map to a key, that map entry can be omitted // An example for a datastore item with a title, summary, rating and identifier: // mapItem: function(/*DatastoreItem*/item){ // // does not include the "tags" and "url" keys // var map = {}; // map["label"] = this.getValue(item, "title", "untitled"); // map["description"] = this.getValue(item, "summary", null); // map["rating"] = this.getValue(item, "rating", "0.0"); // map["id"] = this.getValue(item, "identifier", null); // map["thumbnail"] = this.getValue(item, "thumbnail", null); // return map; // } var map = {}; map.label = "untitled3"; var titleFar = this.getLabel(item); if (titleFar) { if ( typeof titleFar == "string" ) { map.label = titleFar; } else { titleFar.setFinishedCallback(function (title) { map.label = title ? title : "untitled2"; }); titleFar.start(); } } map["description"] = null; var url = this.getValue(item, "URL", ""); map["url"] = "dav:"+url.substring(url.indexOf("dav")+4,url.lastIndexOf("/")); // dav url map["id"] = this.getValue(item, "ID", null); map.thumbnail = ""; var webDavBasePath = com.ibm.mm.enabler.remote.WebDavUrlFactory.createUrl(); var thumbnailPath = item.getMetaData( "thumbnailPath" ); if ( thumbnailPath ) { if( typeof thumbnailPath === "string" ){ map.thumbnail = webDavBasePath + thumbnailPath; } else { thumbnailPath.setFinishedCallback(function (path) { map.thumbnail = webDavBasePath + path; }); thumbnailPath.start(); } } return map; }, /********************************/ /** **/ /** **/ /** dojo.data.api.Identity **/ /** **/ /** **/ /********************************/ getIdentity: function(/* item */ item) { // summary: // Returns a unique identifier for an item. The return value will be // either a string or something that has a toString() method. // item: // The item from the store from which to obtain its identifier. // exceptions: // Conforming implementations may throw an exception or return null if // item is not an item. return this.getValue( "ID" ); }, getIdentityAttributes: function(/* item */ item) { // summary: // Returns an array of attribute names that are used to generate the identity. // For most stores, this is a single attribute, but for some complex stores // such as RDB backed stores that use compound (multi-attribute) identifiers // it can be more than one. If the identity is not composed of attributes // on the item, it will return null. This function is intended to identify // the attributes that comprise the identity so that so that during a render // of all attributes, the UI can hide the the identity information if it // chooses. // item: // The item from the store from which to obtain the array of public attributes that // compose the identifier, if any. return [ "ID" ]; }, fetchItemByIdentity: function(/* object */ keywordArgs) { // summary: // Given the identity of an item, this method returns the item that has // that identity through the onItem callback. Conforming implementations // should return null if there is no item with the given identity. // Implementations of fetchItemByIdentity() may sometimes return an item // from a local cache and may sometimes fetch an item from a remote server, // // keywordArgs: // An anonymous object that defines the item to locate and callbacks to invoke when the // item has been located and load has completed. The format of the object is as follows: // { // identity: string|object, // onItem: Function, // onError: Function, // scope: object // } // The *identity* parameter. // The identity parameter is the identity of the item you wish to locate and load // This attribute is required. It should be a string or an object that toString() // can be called on. // // The *onItem* parameter. // Function(item) // The onItem parameter is the callback to invoke when the item has been loaded. It takes only one // parameter, the item located, or null if none found. // // The *onError* parameter. // Function(error) // The onError parameter is the callback to invoke when the item load encountered an error. It takes only one // parameter, the error object // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onError, etc) will be invoked in the context of the scope object. // In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global. // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global, item, request) keywordArgs.path = keywordArgs.identity.toString(); return this.fetch( keywordArgs ); }, /********************************/ /** **/ /** **/ /** dojo.data.api.Read **/ /** **/ /** **/ /********************************/ getFeatures: function() { // summary: // The getFeatures() method returns an simple keyword values object // that specifies what interface features the datastore implements. // A simple CsvStore may be read-only, and the only feature it // implements will be the 'dojo.data.api.Read' interface, so the // getFeatures() method will return an object like this one: // {'dojo.data.api.Read': true}. // A more sophisticated datastore might implement a variety of // interface features, like 'dojo.data.api.Read', 'dojo.data.api.Write', // 'dojo.data.api.Identity', and 'dojo.data.api.Attribution'. return { 'dojo.data.api.Read': true, 'dojo.data.api.Identity': true }; }, getValue: function( /* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){ var result = defaultValue; var attribute_lc = attribute.toLowerCase(); switch ( attribute_lc ) { case "title": result = this.localizedContext.getTitle( item ); break; case "description": result = this.localizedContext.getDescription( item ); break; case "id": result = item.remoteFile.getName(); break; default: if ( this.hasAttribute( item, attribute ) ) { result = item[ "get" + attribute ](); } } return result; }, getValues: function(/* item */ item, /* attribute-name-string */ attribute) { // summary: // This getValues() method works just like the getValue() method, but getValues() // always returns an array rather than a single attribute value. The array // may be empty, may contain a single attribute value, or may contain many // attribute values. // If the item does not have a value for the given attribute, then getValues() // will return an empty array: []. (So, if store.hasAttribute(item, attribute) // returns false, then store.getValues(item, attribute) will return [].) // // item: // The item to access values on. // attribute: // The attribute to access represented as a string. // // exceptions: // Throws an exception if *item* is not an item, or *attribute* is not a string var result = []; var value = this.getValue( item, attribute, undefined ); if ( value !== undefined ) { result.push( value ); } return result; }, getAttributes: function(/* item */ item) { // summary: // Returns an array with all the attributes that this item has. This // method will always return an array; if the item has no attributes // at all, getAttributes() will return an empty array: []. // // item: // The item to access attributes on. // // exceptions: // Throws an exception if *item* is not an item, or *attribute* is not a string var attr = []; for (var prop in item ) { if (Object.prototype.hasOwnProperty.call( item, prop ) && prop.indexOf( "get" ) === 0 ) { attr.push( prop.substring( 3 ) ); } } return attr; }, hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute) { // summary: // Returns true if the given *item* has a value for the given *attribute*. // // item: // The item to access attributes on. // attribute: // The attribute to access represented as a string. // // exceptions: // Throws an exception if *item* is not an item, or *attribute* is not a string var result = false; if ( item[ "get" + attribute ] !== undefined ) { result = true; } return result; }, containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value) { // summary: // Returns true if the given *value* is one of the values that getValues() // would return. // // item: // The item to access values on. // attribute: // The attribute to access represented as a string. // value: // The value to match as a value for the attribute. // // exceptions: // Throws an exception if *item* is not an item, or *attribute* is not a string var result = false; var values = this.getValues( item, attribute ); if ( values.length > 0 ) { var l = values.length; var i = 0; var found = false; while ( !found && i < l ) { found = ( values[i] === value ); i++; } } return result; }, isItem: function(/* anything */ something) { // summary: // Returns true if *something* is an item and came from the store instance. // Returns false if *something* is a literal, an item from another store instance, // or is any object other than an item. // // something: // Can be anything. // return something !== undefined && something.declaredClass === "com.ibm.mm.enabler.layouttemplate.LayoutTemplate"; }, isItemLoaded: function(/* anything */ something) { // summary: // Returns false if isItem(something) is false. Returns false if // if isItem(something) is true but the the item is not yet loaded // in local memory (for example, if the item has not yet been read // from the server). // // something: // Can be anything. // return this.isItem(something) && something.remoteFile !== undefined; }, loadItem: function(/* object */ keywordArgs) { // summary: // Given an item, this method loads the item so that a subsequent call // to store.isItemLoaded(item) will return true. If a call to // isItemLoaded() returns true before loadItem() is even called, // then loadItem() need not do any work at all and will not even invoke // the callback handlers. So, before invoking this method, check that // the item has not already been loaded. // keywordArgs: // An anonymous object that defines the item to load and callbacks to invoke when the // load has completed. The format of the object is as follows: // { // item: object, // onItem: Function, // onError: Function, // scope: object // } // The *item* parameter. // The item parameter is an object that represents the item in question that should be // contained by the store. This attribute is required. // The *onItem* parameter. // Function(item) // The onItem parameter is the callback to invoke when the item has been loaded. It takes only one // parameter, the fully loaded item. // // The *onError* parameter. // Function(error) // The onError parameter is the callback to invoke when the item load encountered an error. It takes only one // parameter, the error object // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onError, etc) will be invoked in the context of the scope object. // In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global(). // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global(), item, request) }, getLabel: function(/* item */ item) { // summary: // Method to inspect the item and return a user-readable 'label' for the item // that provides a general/adequate description of what the item is. // // description: // Method to inspect the item and return a user-readable 'label' for the item // that provides a general/adequate description of what the item is. In general // most labels will be a specific attribute value or collection of the attribute // values that combine to label the item in some manner. For example for an item // that represents a person it may return the label as: "firstname lastlame" where // the firstname and lastname are attributes on the item. If the store is unable // to determine an adequate human readable label, it should return undefined. Users that wish // to customize how a store instance labels items should replace the getLabel() function on // their instance of the store, or extend the store and replace the function in // the extension class. // // item: // The item to return the label for. // // returns: // A user-readable string representing the item or undefined if no user-readable label can // be generated. return this.localizedContext.getTitle( item ); }, getLabelAttributes: function(/* item */ item) { // summary: // Method to inspect the item and return an array of what attributes of the item were used // to generate its label, if any. // // description: // Method to inspect the item and return an array of what attributes of the item were used // to generate its label, if any. This function is to assist UI developers in knowing what // attributes can be ignored out of the attributes an item has when displaying it, in cases // where the UI is using the label as an overall identifer should they wish to hide // redundant information. // // item: // The item to return the list of label attributes for. // // returns: // An array of attribute names that were used to generate the label, or null if public attributes // were not used to generate the label. return [ "Title", "Description" ]; }, fetch: function(/* Object */ keywordArgs) { // summary: // Given a query and set of defined options, such as a start and count of items to return, // this method executes the query and makes the results available as data items. // The format and expectations of stores is that they operate in a generally asynchronous // manner, therefore callbacks are always used to return items located by the fetch parameters. // // description: // A Request object will always be returned and is returned immediately. // The basic request is nothing more than the keyword args passed to fetch and // an additional function attached, abort(). The returned request object may then be used // to cancel a fetch. All data items returns are passed through the callbacks defined in the // fetch parameters and are not present on the 'request' object. // // This does not mean that custom stores can not add methods and properties to the request object // returned, only that the API does not require it. For more info about the Request API, // see dojo.data.api.Request // // keywordArgs: // The keywordArgs parameter may either be an instance of // conforming to dojo.data.api.Request or may be a simple anonymous object // that may contain any of the following: // { // query: query-string or query-object, // queryOptions: object, // onBegin: Function, // onItem: Function, // onComplete: Function, // onError: Function, // scope: object, // start: int // count: int // sort: array // } // All implementations should accept keywordArgs objects with any of // the 9 standard properties: query, onBegin, onItem, onComplete, onError // scope, sort, start, and count. Some implementations may accept additional // properties in the keywordArgs object as valid parameters, such as // {includeOutliers:true}. // // The *query* parameter. // The query may be optional in some data store implementations. // The dojo.data.api.Read API does not specify the syntax or semantics // of the query itself -- each different data store implementation // may have its own notion of what a query should look like. // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data // and dojox.data support an object structure query, where the object is a set of // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the // dijit widgets, such as ComboBox assume this to be the case when working with a datastore // when they dynamically update the query. Therefore, for maximum compatibility with dijit // widgets the recommended query parameter is a key/value object. That does not mean that th // the datastore may not take alternative query forms, such as a simple string, a Date, a number, // or a mix of such. Ultimately, The dojo.data.api.Read API is agnostic about what the query // format. // Further note: In general for query objects that accept strings as attribute // value matches, the store should also support basic filtering capability, such as * // (match any character) and ? (match single character). An example query that is a query object // would be like: { attrFoo: "value*"}. Which generally means match all items where they have // an attribute named attrFoo, with a value that starts with 'value'. // // The *queryOptions* parameter // The queryOptions parameter is an optional parameter used to specify options that may modify // the query in some fashion, such as doing a case insensitive search, or doing a deep search // where all items in a hierarchical representation of data are scanned instead of just the root // items. It currently defines two options that all datastores should attempt to honor if possible: // { // ignoreCase: boolean, //Whether or not the query should match case sensitively or not. Default behaviour is false. // deep: boolean //Whether or not a fetch should do a deep search of items and all child // //items instead of just root-level items in a datastore. Default is false. // } // // The *onBegin* parameter. // function(size, request); // If an onBegin callback function is provided, the callback function // will be called just once, before the first onItem callback is called. // The onBegin callback function will be passed two arguments, the // the total number of items identified and the Request object. If the total number is // unknown, then size will be -1. Note that size is not necessarily the size of the // collection of items returned from the query, as the request may have specified to return only a // subset of the total set of items through the use of the start and count parameters. // // The *onItem* parameter. // function(item, request); // If an onItem callback function is provided, the callback function // will be called as each item in the result is received. The callback // function will be passed two arguments: the item itself, and the // Request object. // // The *onComplete* parameter. // function(items, request); // // If an onComplete callback function is provided, the callback function // will be called just once, after the last onItem callback is called. // Note that if the onItem callback is not present, then onComplete will be passed // an array containing all items which matched the query and the request object. // If the onItem callback is present, then onComplete is called as: // onComplete(null, request). // // The *onError* parameter. // function(errorData, request); // If an onError callback function is provided, the callback function // will be called if there is any sort of error while attempting to // execute the query. // The onError callback function will be passed two arguments: // an Error object and the Request object. // // The *scope* parameter. // If a scope object is provided, all of the callback functions (onItem, // onComplete, onError, etc) will be invoked in the context of the scope // object. In the body of the callback function, the value of the "this" // keyword will be the scope object. If no scope object is provided, // the callback functions will be called in the context of dojo.global(). // For example, onItem.call(scope, item, request) vs. // onItem.call(dojo.global(), item, request) // // The *start* parameter. // If a start parameter is specified, this is a indication to the datastore to // only start returning items once the start number of items have been located and // skipped. When this parameter is paired with 'count', the store should be able // to page across queries with millions of hits by only returning subsets of the // hits for each query // // The *count* parameter. // If a count parameter is specified, this is a indication to the datastore to // only return up to that many items. This allows a fetch call that may have // millions of item matches to be paired down to something reasonable. // // The *sort* parameter. // If a sort parameter is specified, this is a indication to the datastore to // sort the items in some manner before returning the items. The array is an array of // javascript objects that must conform to the following format to be applied to the // fetching of items: // { // attribute: attribute || attribute-name-string, // descending: true|false; // Optional. Default is false. // } // Note that when comparing attributes, if an item contains no value for the attribute // (undefined), then it the default ascending sort logic should push it to the bottom // of the list. In the descending order case, it such items should appear at the top of the list. // // returns: // The fetch() method will return a javascript object conforming to the API // defined in dojo.data.api.Request. In general, it will be the keywordArgs // object returned with the required functions in Request.js attached. // Its general purpose is to provide a convenient way for a caller to abort an // ongoing fetch. // // The Request object may also have additional properties when it is returned // such as request.store property, which is a pointer to the datastore object that // fetch() is a method of. // // exceptions: // Throws an exception if the query is not valid, or if the query // is required but was not supplied. if ( keywordArgs === undefined || keywordArgs === null ) { keywordArgs = {}; } var query = keywordArgs.query; var keywordSearch = query ? query.keywords : undefined; var onBegin = keywordArgs.onBegin; var onItem = keywordArgs.onItem; var onComplete = keywordArgs.onComplete; var onError = keywordArgs.onError; var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global; var start = keywordArgs.start ? keywordArgs.start : 0; var count = keywordArgs.count ? keywordArgs.count : Number.POSITIVE_INFINITY; var sort = keywordArgs.sort ? keywordArgs.sort : undefined; var caseInsensitive = keywordArgs.queryOptions ? keywordArgs.queryOptions.ignoreCase : undefined; var sortAttribute; var isSortDescending; if ( sort ) { sortAttribute = sort.attribute; isSortDescending = sort.descending; } else { sortAttribute = "id"; } var request = keywordArgs; request.abort = function () { //any way to abort an Enabler Deferred? this.aborted = true; }; //These odd looking function definitions simply ensure that all of the possible events are defined to a valid //function - even if it's an empty function. This simplifies the iteration code a bit, saving a few conditional //checks later on. var makeOnBeginCall = onBegin ? function (size) { if ( !request.aborted ) { onBegin.call( scope, size, request ); } } : function () {}; //Determines whether or not an item should be accepted into the result set. var me = this; var itemAcceptFunction = function ( item ) { var shouldCall = !request.aborted; if ( keywordSearch && keywordSearch.length > 0) { var id = me.mapItem(item).label; if ( caseInsensitive ) { id = id.toUpperCase(); } var len = keywordSearch.length; var keyword; for ( var i = 0; i < len; i++ ) { keyword = keywordSearch[i]; if ( caseInsensitive ) { keyword = keyword.toUpperCase(); } if ( id.indexOf( keyword ) < 0 ) { return false; } } } return shouldCall; }; var makeOnItemCall = onItem ? function ( item ) { onItem.call( scope, item, request ); } : function () {}; var me = this; var makeOnCompleteCall = onComplete ? function ( items ) { if ( !request.aborted ) { if ( onItem ) { onComplete.call( scope, null, request ); } else { //Sorts based on the given attribute. The given attribute must be one of the attributes provided by //this.mapItem(). if ( sortAttribute ) { var lc_sortAttribute = sortAttribute.toLowerCase(); //Control sort order based on the provided options. var lessThan = isSortDescending ? 1 : -1; var greaterThan = isSortDescending ? -1 : 1; var equal = 0; var sortedItems = items.sort(function (a,b) { var retVal = undefined; var item1 = me.mapItem(a); var item2 = me.mapItem(b); var value1 = item1[ lc_sortAttribute ]; var value2 = item2[ lc_sortAttribute ]; //Sorting logic: //According to the dojo data API definition, any undefined values go to the end for //ascending sort order and the beginning for descending sort order. We return a -1 if value1 //is less than value2, a 0 if value1 is equal to value2, or a +1 if value1 is greater than //value2. The return value is reversed (via the local variables declared outside of this //function) if the sort order is descending (ascending is the default). if ( !value1 && value2 ) { retVal = greaterThan; } else if ( !value1 && !value2 ) { retVal = equal; } else if ( value1 && !value2 ) { retVal = lessThan; } else if ( value1 === value2 ) { retVal = equal; } else if ( value1 > value2 ) { retVal = greaterThan; } else if ( value1 < value2 ) { retVal = lessThan; } else { retVal = equal; } return retVal; }); //Make sure we didn't have a failure or lose an item along the way - probably better to return //out of order items instead of nothing or an incomplete result set. if ( sortedItems !== null && sortedItems.length === items.length ) { items = sortedItems.splice( start, count ); } } onComplete.call( scope, items, request ); } } } : function () {}; var makeOnBeginCall = onBegin ? function ( size ) { if ( !request.aborted ) { onBegin.call(scope, size, request ); } } : function () {}; var path = query ? query.path : query; this._loadItems( path, start, count, itemAcceptFunction, makeOnBeginCall, makeOnItemCall, makeOnCompleteCall ); return request; }, _loadItems: function ( path, start, count, itemAcceptFunction, makeOnBeginCall, makeOnItemCall, makeOnCompleteCall ) { //No specific path is specified or all items are requested. if ( path === undefined || path === null || path === "*" ) { var me = this; var it = this.model.iterator(); //The iterator has to load before it can be used. Can switch to setForEachCallback once that is fully implemented. it.setFinishedCallback(function () { var size = it.size(); size.setFinishedCallback( function ( totalCount ) { var results = []; var pending = 0; returnedCount = 0; var delayedStarts = []; var threshold = 2; while ( it.hasNext() ) { var item = it.next(); item.setFinishedCallback(function (object) { if ( object && itemAcceptFunction( object ) ) { results.push( object ); //Before an item is fully "loaded", we need to ensure that all remote artifacts are //loaded since the widgets make no allowance for a title or description being an //asynchronous call away. Requesting the title here forces the item to load the remote //properties file for the current locale that defines the title and description. var locale = me.localizedContext.getLocale( object ); //The available locales are local to the item once the initial PROPFIND occurs. If a //title exists for the current configured locale, then this item isn't finished until //that remote properties file is loaded. Otherwise, just be done. if ( locale ) { var d = object.getTitle( locale ); d.setFinishedCallback(function () { var d2 = object.getMetaData( "" ); d2.setFinishedCallback(function () { pending--; makeOnItemCall( object ); returnedCount++; if ( pending < threshold && delayedStarts.length > 0 ) { pending++; delayedStarts.pop().start(); } if ( returnedCount == totalCount ) { // make callbacks makeOnBeginCall( results.length ); makeOnCompleteCall( results ); } }); d2.start(); }); d.start(); } else { pending--; makeOnItemCall( object ); } } else { returnedCount++; pending--; if ( pending < threshold && delayedStarts.length > 0 ) { pending++; delayedStarts.pop().start(); } if ( returnedCount == totalCount ) { // make callbacks makeOnBeginCall( results.length ); makeOnCompleteCall( results ); } } }); if ( pending >= threshold ) { delayedStarts.push( item ); } else { pending++; item.start(); } } }); size.start(); }); it.start(); } else { var deferred = this.model.find( path ); deferred.setFinishedCallback(function (object) { makeOnBeginCall( 1 ); makeOnItemCall( object ); makeOnCompleteCall( object ); }); deferred.start(); } } /********************************/ /** **/ /** **/ /** dojo.data.api.Write **/ /** **/ /** **/ /********************************/ // deleteItem: function(/* item */ item){ // // summary: // // Deletes an item from the store. // // // // item: // // The item to delete. // // // // exceptions: // // Throws an exception if the argument *item* is not an item // // (if store.isItem(item) returns false). // // example: // // | var success = store.deleteItem(kermit); // var success = false; // // if ( this.isItem( item ) ) { // this.model.remove( item ); // success = true; // } // // return success; // }, // setValue: function( /* item */ item, // /* string */ attribute, // /* almost anything */ value){ // // summary: // // Sets the value of an attribute on an item. // // Replaces any previous value or values. // // // // item: // // The item to modify. // // attribute: // // The attribute of the item to change represented as a string name. // // value: // // The value to assign to the item. // var attribute = attribute.toLowerCase(); // var success = false; // switch ( attribute ) { // case "title": // item.setTitle( value.locale, value.string ); // success = true; // break; // case "description": // item.setDescription( value.locale, value.string ); // success = true; // break; // case "metadata": // item.setMetaData( value.name, value.value ); // success = true; // break; // } // // return success; // }, // setValues: function(/* item */ item, /* string */ attribute, /* array */ values) { // // summary: // // Adds each value in the *values* array as a value of the given // // attribute on the given item. // // Replaces any previous value or values. // // Calling store.setValues(x, y, []) (with *values* as an empty array) has // // the same effect as calling store.unsetAttribute(x, y). // // // // item: // // The item to modify. // // attribute: // // The attribute of the item to change represented as a string name. // // values: // // An array of values to assign to the attribute.. // // // // exceptions: // // Throws an exception if *values* is not an array, if *item* is not an // // item, or if *attribute* is neither an attribute object or a string. // // examples: // // var success = store.setValues(kermit, "color", ["green", "aqua"]); // // success = store.setValues(kermit, "color", []); // // if (success) {assert(!store.hasAttribute(kermit, "color"));} // var l = values.length; // var success = true; // for ( var i = 0; i < l; i++ ) { // success = success && this.setValue( item, attribute, values[i] ); // } // // return success; // }, // newItem: function(/* Object */ keywordArgs, /*Object?*/ parentInfo) { // // summary: // // Returns a newly created item. Sets the attributes of the new // // item based on the *keywordArgs* provided. In general, the attribute // // names in the keywords become the attributes in the new item and as for // // the attribute values in keywordArgs, they become the values of the attributes // // in the new item. In addition, for stores that support hierarchical item // // creation, an optional second parameter is accepted that defines what item is the parent // // of the new item and what attribute of that item should the new item be assigned to. // // In general, this will assume that the attribute targetted is multi-valued and a new item // // is appended onto the list of values for that attribute. // // // // keywordArgs: // // A javascript object defining the initial content of the item as a set of JavaScript 'property name: value' pairs. // // parentInfo: // // An optional javascript object defining what item is the parent of this item (in a hierarchical store. Not all stores do hierarchical items), // // and what attribute of that parent to assign the new item to. If this is present, and the attribute specified // // is a multi-valued attribute, it will append this item into the array of values for that attribute. The structure // // of the object is as follows: // // { // // parent: someItem, // // attribute: "attribute-name-string" // // } // // // // exceptions: // // Throws an exception if *keywordArgs* is a string or a number or // // anything other than a simple anonymous object. // // Throws an exception if the item in parentInfo is not an item from the store // // or if the attribute isn't an attribute name string. // // examples: // // var kermit = store.newItem({name: "Kermit", color:[blue, green]}); // var path = keywordArgs.path; // var metadata = keywordArgs.metadata; // var titles = keywordArgs.titles; // var descriptions = keywordArgs.descriptions; // // var item = this.model.create({ name: path }); // if ( metadata ) { // for (var prop in metadata ) { // if (Object.prototype.hasOwnProperty.call( metadata, prop ) ) { // item.setMetaData( prop, metadata[prop] ); // } // } // } // if ( titles ) { // for (var locale in titles ) { // if (Object.prototype.hasOwnProperty.call( titles, locale ) ) { // item.setMetaData( locale, titles[locale] ); // } // } // } // if ( descriptions ) { // for (var locale in descriptions ) { // if (Object.prototype.hasOwnProperty.call( descriptions, locale ) ) { // item.setMetaData( locale, descriptions[locale] ); // } // } // } // return item; // }, // save: function(/* object */ keywordArgs) { // // summary: // // Saves to the server all the changes that have been made locally. // // The save operation may take some time and is generally performed // // in an asynchronous fashion. The outcome of the save action is // // is passed into the set of supported callbacks for the save. // // // // keywordArgs: // // { // // onComplete: function // // onError: function // // scope: object // // } // // // // The *onComplete* parameter. // // function(); // // // // If an onComplete callback function is provided, the callback function // // will be called just once, after the save has completed. No parameters // // are generally passed to the onComplete. // // // // The *onError* parameter. // // function(errorData); // // // // If an onError callback function is provided, the callback function // // will be called if there is any sort of error while attempting to // // execute the save. The onError function will be based one parameter, the // // error. // // // // The *scope* parameter. // // If a scope object is provided, all of the callback function ( // // onComplete, onError, etc) will be invoked in the context of the scope // // object. In the body of the callback function, the value of the "this" // // keyword will be the scope object. If no scope object is provided, // // the callback functions will be called in the context of dojo.global. // // For example, onComplete.call(scope) vs. // // onComplete.call(dojo.global) // // // // returns: // // Nothing. Since the saves are generally asynchronous, there is // // no need to return anything. All results are passed via callbacks. // // examples: // // store.save({onComplete: onSave}); // // store.save({scope: fooObj, onComplete: onSave, onError: saveFailed}); // var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global; // var d = this.model.commit(); // d.setFinishedCallback(function () { // onComplete.call( scope ); // }); // }, // revert: function() { // // summary: // // Discards any unsaved changes. // // description: // // Discards any unsaved changes. // // // // examples: // // var success = store.revert(); // this.model._revert(); // return true; // }, // isDirty: function(/* item? */ item) { // // summary: // // Given an item, isDirty() returns true if the item has been modified // // since the last save(). If isDirty() is called with no *item* argument, // // then this method returns true if any item has been modified since // // the last save(). // // // // item: // // The item to check. // // // // exceptions: // // Throws an exception if isDirty() is passed an argument and the // // argument is not an item. // // examples: // // var trueOrFalse = store.isDirty(kermit); // true if kermit is dirty // // var trueOrFalse = store.isDirty(); // true if any item is dirty // var result = false; // // if ( item ) { // result = item._isDirty(); // } else { // result = this.model.isDirty(); // } // // return result; // } }); } if(!dojo._hasResource["com.ibm.pb.data.TaggedItemStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.pb.data.TaggedItemStore"] = true; dojo.provide("com.ibm.pb.data.TaggedItemStore"); dojo.declare("com.ibm.pb.data.TaggedItemStore", [com.ibm.data.OpenSearchFeedStore,com.ibm.data.CatalogMixin], { itemClass: "com.ibm.data.TaggedItem", searchable: false, noIcons: true, prefix: "rm:pdl:oid:", roleLevel: null, namespaces: dojo.mixin({}, com.ibm.data.OpenSearchFeedStore.prototype.namespaces, { "model" : "http://www.ibm.com/xmlns/prod/websphere/portal/v6.0.1/portal-model-elements", "base" : "http://www.ibm.com/xmlns/prod/websphere/portal/v6.0/ibm-portal-composite-base", "thr" : "http://purl.org/syndication/thread/1.0", "xhtml" : "http://www.w3.org/1999/xhtml", "xsi" : "http://www.w3.org/2001/XMLSchema-instance", "portal" : "http://www.ibm.com/xmlns/prod/websphere/portal/v6.0.1/portal-model" }), mapItem: function(/*item*/item){ // summary: get the id, label and description for the item var map = {}; map.id = this.getEPResolvedValue(item, "id", null); if(map.id) { map.id = map.id.substring(this.prefix.length); } // strips off the "pdl:" map.label = this.getEPResolvedValue(item, "title", "untitled"); map.description = this.getEPResolvedValue(item, "summary", ""); return map; }, prepareQuery: function(keywordArgs) { // summary: convert the count and start parameters from keywordArgs to num and start parameters in keywordArgs.query if(!keywordArgs.query) { keywordArgs.query = {}; } if(keywordArgs.count){ keywordArgs.query["max-results"] = keywordArgs.count; delete keywordArgs.count; } if(keywordArgs.start >= 0){ keywordArgs.query["start-index"] = keywordArgs.start; delete keywordArgs.start; } return keywordArgs; }, validateSelf: function(){ if(!this.roleLevel) return true; var navModel = com.ibm.mashups.enabler.navigation.Factory.getNavigationModel(), pageId = com.ibm.mashups.builder.model.Factory.getRuntimeModel().getCurrentPage().getID(), page = navModel.find(pageId).start(); return page.hasRole(this.roleLevel); }, getSourceLabel: function(){ if(!this.category) return false; var categories = this._getCategories(); for (var i = 0; i < categories.length; i++) { var category = categories[i]; var id = category.getID(); if(id.indexOf(this.category) == 0) return com.ibm.mm.builder.utils.htmlUtil.escapeString(category.getTitle(dojo.locale || ibmConfig["default.locale"])); } return false; }, _getCategories: function(){ if (this.catalogEntries) { return this.catalogEntries; } this.catalogEntries = []; var cm = com.ibm.mashups.enabler.model.Factory.getCatalogCategoryModel(), rootNode = cm.getRoot().start(), iter = cm.getChildren(rootNode); while (iter.hasNext()) { this.catalogEntries.push(iter.next()); } return this.catalogEntries; } }); dojo.declare("com.ibm.data.TaggedItem", com.ibm.data.OpenSearchFeedItem, { namespaces: dojo.mixin({}, com.ibm.pb.data.TaggedItemStore.prototype.namespaces) } ); } if(!dojo._hasResource["com.ibm.pb.data.WcmStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["com.ibm.pb.data.WcmStore"] = true; dojo.provide("com.ibm.pb.data.WcmStore"); dojo.declare("com.ibm.pb.data.WcmStore", [com.ibm.pb.data.InstalledPortletStore], { validateSelf: function(){ // only show the category if the page has a mapping OR the user has at least editor access (to add a mapping) if(com.ibm.pb.customize.enabler.AddContentController._pageHasContentMapping()) return true; var navModel = com.ibm.mashups.enabler.navigation.Factory.getNavigationModel(), pageId = com.ibm.mashups.builder.model.Factory.getRuntimeModel().getCurrentPage().getID(), page = navModel.find(pageId).start(); return page.hasRole(com.ibm.mashups.enabler.ac.RoleType.EDITOR); } }); }